求逆元方法总结

http://blog.youkuaiyun.com/hlyfalsy/article/details/38067021


在MOD的情况下,  (a*b/c ) %MOD  不能直接 / c 来求,需要找到一个数 inv 使得  inv * c % MOD = 1 。 这样 (a*b / c) % MOD  = (a * b * inv) % MOD;


性质: 逆元是积性函数   存在  a*b = c ,那么  inv[c] = inv[a] * inv[b] % MOD;


1、  循环找解的方法

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. long long circleRun(long long n){  
  2.     for(long long i = 1;i < MOD;i++)  
  3.         if(i * n % MOD == 1)  
  4.             return i;  
  5.     return -1;  
  6. }  
  7. long long n;  
  8. int main(){  
  9.       
  10.     while(cin >> n){  
  11.         cout << n << " 's inv is "<<endl;  
  12.         cout << circleRun(n) << endl;  
  13.     }     
  14.     return 0;  
  15. }  

2、费马小定理的解法  MOD 是质数才能用

利用   a ^ (p-1) % MOD === 1 , 那么它的逆元就是     a ^ (p-2)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. #include<iostream>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. const int MOD = 1e9+7;  
  8.   
  9.   
  10. long long quickpow(long long base,long long n){  
  11.     long long ans = 1;  
  12.     while(n){  
  13.         if(n%2 == 1) ans = ans * base % MOD;  
  14.         n /= 2;  
  15.         base = base * base % MOD;  
  16.     }  
  17.     return ans;  
  18. }  
  19. long long n;  
  20. int main(){  
  21.       
  22.     while(cin >> n){  
  23.         cout << n << " 's inv is "<<endl;  
  24.         //cout << circleRun(n) << endl;  
  25.         cout << " a ^ (p-2) % MOD "<< endl;  
  26.         cout << quickpow(n, MOD-2) << endl;  
  27.           
  28.     }     
  29.     return 0;  
  30. }  

3、利用欧几里德扩展来求 ,

欧几里德扩展 是用来解决  ax + by = gcd(a,b)这样的等式。

这时候取  b = MOD, 你可以写成这样  ax = gcd(a,b) - by

推导出 a*x % MOD = gcd(a,b) %MOD

所以只要  gcd(a,b) % MOD === 1时,就可以使用这条来求a的逆元

但用exgcd求得时候,inv可能是负数, 还需要进行如下操作

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. inv = (inv % MOD + MOD) % MOD;  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. long long exGcd(long long a, long long b, long long &x0, long long &y0) // a*x0 + b*y0 = gcd(a,b)  
  2. {  
  3.     if(b==0)  
  4.     {  
  5.       x0 = 1;  
  6.       y0 = 0;  
  7.       return a;  
  8.     }  
  9.     long long r = exGcd(b, a % b, x0, y0);  
  10.     long long t = x0;  
  11.     x0 = y0;  
  12.     y0 = t - a / b * y0;  
  13.     return r;  
  14. }  
  15.    
  16. long long n;  
  17. int main(){  
  18.       
  19.     while(cin >> n){  
  20.         cout << n << " 's inv is "<<endl;  
  21.         //cout << circleRun(n) << endl;  
  22.         cout << " a ^ (p-2) % MOD "<< endl;  
  23.         cout << quickpow(n, MOD-2) << endl;  
  24.           
  25.         cout << " ax + by = gcd(a,b) " << endl;  
  26.         long long inv,y0;  
  27.         exGcd(n ,MOD,inv,y0);  
  28.         inv = (inv % MOD + MOD) % MOD;  
  29.         cout << inv << endl;  
  30.     }     
  31.     return 0;  
  32. }  


4、利用某神奇推导。。 O(n)求出 1---- n 的所有逆元。 

预处理1-n关于p的逆元:(n < p) , 因为 逆元是积性函数,所以只要 p > n 成立即可,而不需要p必须为素数


         假设已经预处理了1-i-1的逆元,j的逆元设为F[j]


         令p = x * i –y ( 0 < y < i)


         X* i = y (mod p)


         X* F[y] * i = y * F[y] = 1(mod p)


         所以i的逆元是F[i] = X* F[y]


         这样就可以O(n)的时间预处理了。

参考链接  

代码实现

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. const int N = 100005;  
  2. long long _inv[N];  
  3. void pre() {  
  4.     _inv[0] = _inv[1] = 1;  
  5.     for (int i = 2; i < N; i++) {  
  6.         _inv[i] = ((MOD - MOD / i) * _inv[MOD % i]) % MOD;  
  7.     }  
  8. }  
  9. long long n;  
  10. int main(){  
  11.         pre();  
  12.     while(cin >> n){  
  13.         cout << _inv[n] << endl;  
  14.     }  
  15.         return 0;  
  16. }  


4、 利用逆元函数是完全积性函数的性质

求所有质数的逆元即可,预处理复杂度是O(n / logn * logn) = O(n)

这种写法可以用 exgcd 来实现素数的logn 求逆,因为此时  a = p ,  MOD无论取何值(p除外)  ,  都有  gcd(p ,mod) = 1,适合通用情况。

之后再采取质因数分解的方法,即可对任意一个 n 以 logn速度求出其逆元 

不过。。ACM竞赛如果为了求逆写这么长的代码貌似不太现实。



### C++ 求模逆元的实现方法 #### 方法一:费马小定理法 如果模数 \( p \) 是一个质数,则可以利用费马小定理来求解逆元。根据该理论,对于任意整数 \( a \),满足条件 \( (a^{p-1} \equiv 1 \ (\text{mod}\ p))\) 的情况下,\( a^{-1} \equiv a^{p-2} \ (\text{mod}\ p)\)[^2]。 以下是基于此原理的代码示例: ```cpp #include <iostream> using namespace std; typedef long long ll; // 快速幂函数 ll fast_pow(ll base, ll exp, ll mod) { ll result = 1; while(exp > 0){ if(exp & 1) result = (result * base) % mod; exp >>= 1; base = (base * base) % mod; } return result; } int main(){ ios::sync_with_stdio(false); cin.tie(NULL); ll num, prime_mod; cin >> num >> prime_mod; // 输入数值num以及素数prime_mod if(__gcd(num, prime_mod) != 1){ cout << "-1"; // 如果不互质则无逆元 return 0; } cout << fast_pow(num, prime_mod - 2, prime_mod); // 输出结果 } ``` 上述代码通过快速幂运算实现了费马小定理中的指数部分计算。 --- #### 方法二:扩展欧几里得算法 另一种常用的方法是使用扩展欧几里得算法求解线性同余方程 \( ax + py = 1 \) 中的未知量 \( x \) 和 \( y \) 。其中,当且仅当 \( gcd(a, p) = 1 \) (即 \( a \) 和 \( p \) 互质),存在唯一解使得 \( x \% p \) 即为所求逆元[^3]。 下面是具体实现代码: ```cpp #include <bits/stdc++.h> using namespace std; // 扩展欧几里德算法 void extended_gcd(int a, int b, int& d, int& x, int& y){ if(!b){ d = a; x = 1; y = 0; } else{ extended_gcd(b, a % b, d, y, x); y -= x * (a / b); } } int modular_inverse(int a, int m){ int d, x, y; extended_gcd(a, m, d, x, y); if(d != 1) return -1; // 若d!=1表示不存在逆元 else return ((x % m) + m) % m; // 返回正数形式的结果 } int main(){ int a, m; cin >> a >> m; int inv = modular_inverse(a, m); if(inv == -1) cout << "No inverse exists."; else cout << "The modular inverse is: " << inv; } ``` 这段程序定义了一个 `extended_gcd` 函数用于执行扩展欧几里得算法,并借助它完成对给定参数下是否存在并找出其乘法逆元的任务。 --- #### 总结说明 两种主要方式各有优劣,在实际应用过程中需视具体情况而定。若已知模数必然是质数可优先考虑效率更高的前者;反之后者更为通用但也稍显复杂一些[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值