各種求逆元方法總結[轉]
各種求逆元方法總結[轉]
在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,longlong 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競賽如果為了求逆寫這麽長的代碼貌似不太現實。
各種求逆元方法總結[轉]