【算法模板】数论:乘法逆元求法二则

概念

同余式

同余式是数论中的一个重要概念。若两个整数 a 和 b 除以正整数 n 所得的余数相同,则称 a 与 b 在模 n 意义下同余,记作: a≡b (mod n)

例如,17≡5 (mod 12) 因为 17 和 5 除以 12 的余数都是 5。

乘法逆元

在模 n 意义下,一个数 a 的乘法逆元是指一个整数 x,使得: a⋅x≡1 (mod n)

若 a 和 n 互质(即它们的最大公约数为 1),则 a 在模 n 意义下一定存在乘法逆元。

求乘法逆元的一种常用方法是使用扩展欧几里得算法,这个算法不仅可以求出两个数的最大公约数,还可以找到使得 ax+by=gcd(a,b) 的整数 x 和 y。

费马小定理

费马小定理是数论中的一个重要定理。其内容是: 如果 p 是一个质数,而 a 是一个不被 p 整除的整数,则: a^(p−1)≡1 (mod p)

换句话说,任何不被质数 p 整除的整数 a 的 p−1 次幂,除以 p 的余数是 1。

费马小定理的一个重要应用是求模 p 的乘法逆元。假设 p 是质数,且 a 是一个不被 p 整除的整数,根据费马小定理: a^(p−1)≡1 (mod p) 将上式两边同时乘以 a^−1,得到: a^(p−2)≡a−1 (mod p)

因此,a 在模 p 意义下的乘法逆元是 a^(p−2)。

扩展欧几里得算法

扩展欧几里得算法(Extended Euclidean Algorithm)是一种用来计算两个整数 a 和 b 的最大公约数(gcd)以及同时找到满足贝祖等式 ax+by=gcd(a,b) 的整数 x 和 y 的算法。这个算法不仅可以用于计算 gcd,还可以解决一些数论问题,如求解线性同余方程和计算模逆元等。

对于模 m 下的乘法逆元 a^−1 ,我们希望找到一个整数 x ,使得: ax≡1 (mod m) 即: ax+my=1 这表明 gcd(a,m)=1。

算法模板

cpp 复制代码
// 快速幂算法
int power(int base, int exp, int mod) {
    int result = 1;
    base = base % mod; // 处理 base 大于 mod 的情况
    while (exp > 0) {
        if (exp & 1) {
            result = (result * base) % mod;
        }
        exp = exp >> 1;
        base = (base * base) % mod;
    }
    return result;
}

// 使用费马小定理求乘法逆元
int modInverse(int a, int p) {
    if (a == 0) return -1; // 逆元不存在
    // 根据费马小定理计算逆元
    return power(a, p - 2, p);
}
cpp 复制代码
// 扩展欧几里得算法
int extendedGCD(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int gcd = extendedGCD(b, a % b, y, x);
    y -= (a / b) * x;
    return gcd;
}

// 求乘法逆元
int modInverse(int a, int m) {
    int x, y;
    int gcd = extendedGCD(a, m, x, y);
    if (gcd != 1) return -1; // 逆元不存在
    else {
        // 确保结果是正数
        return (x % m + m) % m;
    }
}

例题

#110. 乘法逆元 - 题目 - LibreOJ (loj.ac)(卡cout

给定正整数 n 与 p,求 1 ~ n 中的所有数在模 p 意义下的乘法逆元,p 为质数。

c 复制代码
#include <stdio.h>
#define int long long

int power(int base, int exp, int mod);
int modInverse(int a, int p);

signed main() {
    int n,p;
    scanf("%lld %lld",&n,&p);
    for(int i=1;i<=n;i++)
        printf("%lld\n",modInverse(i,p));
     return 0;
}
cpp 复制代码
#include <stdio.h>
#define int long long

int extendedGCD(int a, int b, int &x, int &y);
int modInverse(int a, int m);

signed main() {
    int n,p;
    scanf("%lld %lld",&n,&p);
    for(int i=1;i<=n;i++)
        printf("%lld\n",modInverse(i,p));
    return 0;
}

*对比两种求乘法逆元的方法

费马小定理

使用费马小定理求乘法逆元的过程主要依赖于快速幂算法,时间复杂度为 O(log⁡p)O(\log p)O(logp),其中 ppp 是模数。

  • 步骤:计算 a^(p−2) mod  p ,这个计算过程使用快速幂算法。

因此,总体时间复杂度为 O(logp)。

扩展欧几里得算法

扩展欧几里得算法的时间复杂度为 O(loga+logb),其中 a 和 b 是你要计算的两个数。通常在求 a 在模 m 下的逆元时,b 可以看作是模 m。

  • 步骤:在扩展欧几里得算法中,我们通过递归或迭代来计算 gcd(a,m),并同时找到满足 ax+my=gcd(a,m) 的 x 和 y。

因此,总体时间复杂度为 O(logm),其中 m 是模数。

总结

  • 费马小定理:时间复杂度为 O(logp),其中 ppp 是质数模数。
  • 扩展欧几里得算法:时间复杂度为 O(logm),其中 m 是模数。

在实际应用中,如果模数 p 是质数且已知,费马小定理通常会更高效,因为只需要进行一次快速幂运算。而扩展欧几里得算法在模数不为质数的情况下仍然适用,且其处理范围更广。

相关推荐
旺小仔.19 分钟前
【数据结构篇】~排序(1)之插入排序
c语言·数据结构·算法·链表·性能优化·排序算法
绎岚科技44 分钟前
深度学习自编码器 - 随机编码器和解码器篇
人工智能·深度学习·算法·机器学习
jingling5551 小时前
后端开发刷题 | 数字字符串转化成IP地址
java·开发语言·javascript·算法
云边有个稻草人1 小时前
【刷题】Day5--数字在升序数组中出现的次数
开发语言·笔记·算法
ymchuangke1 小时前
评价类——熵权法(Entropy Weight Method, EWM),完全客观评价
人工智能·python·算法·机器学习·数学建模
Crossoads2 小时前
【数据结构】排序算法---希尔排序
c语言·开发语言·数据结构·算法·排序算法
skaiuijing2 小时前
巧用二级指针
c语言·开发语言·算法·架构·操作系统
jingling5552 小时前
后端开发刷题 | 最长上升子序列
java·开发语言·数据结构·后端·算法·动态规划
YYDS3142 小时前
C++战列舰小游戏Lv. 1.4版本(半成品)
开发语言·数据库·c++·算法·游戏·游戏程序
ymchuangke3 小时前
蚁群算法(ACO算法)求解实例---旅行商问题 (TSP)
人工智能·python·算法·机器学习·数学建模