码迷,mamicode.com
首页 > 其他好文 > 详细

各种求逆元方法总结[转]

时间:2017-10-04 21:12:45      阅读:352      评论:0      收藏:0      [点我收藏+]

标签:直接   base   一个   ring   long   因数分解   family   情况   总结   

各种求逆元方法总结[转]

在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、  循环找解的方法

 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)

 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可能是负数, 还需要进行如下操作inv = (inv % MOD + MOD) % MOD;

 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     while(cin >> n){
19         cout << n << " ‘s inv is "<<endl;
20         //cout << circleRun(n) << endl;
21         cout << " a ^ (p-2) % MOD "<< endl;
22         cout << quickpow(n, MOD-2) << endl;
23         
24         cout << " ax + by = gcd(a,b) " << endl;
25         long long inv,y0;
26         exGcd(n ,MOD,inv,y0);
27         inv = (inv % MOD + MOD) % MOD;
28         cout << inv << endl;
29     }    
30     return 0;
31 }

 

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)的时间预处理了。

 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竞赛如果为了求逆写这么长的代码貌似不太现实。

各种求逆元方法总结[转]

标签:直接   base   一个   ring   long   因数分解   family   情况   总结   

原文地址:http://www.cnblogs.com/whc200305/p/7627143.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!