1. 程式人生 > >各種求逆元方法總結[轉]

各種求逆元方法總結[轉]

str com 情況 sans esp 找到 解法 () clu

各種求逆元方法總結[轉]

在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競賽如果為了求逆寫這麽長的代碼貌似不太現實。

各種求逆元方法總結[轉]