乘法逆元模板
乘法逆元及其求法
1.乘法逆元定義:在wiki中也叫倒數,當然是% p 後的,其實就是倒數。如果ax≡1(mod p),且gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元為x。
在求解除法取模問題(a/b)%m時,我們可以轉化為(a%(b∗m))/b,
但是如果b很大,則會出現爆精度問題,所以我們避免使用除法直接計算。
可以使用逆元將除法轉換為乘法:
假設b存在乘法逆元,即與m互質(充要條件)。設c是b的逆元,即b∗c≡1(modm),那麽有a/b=(a/b)∗1=(a/b)∗b∗c=a∗c(modm)
即,除以一個數取模等於乘以這個數的逆元取模。
- 逆元求解一般利用擴歐。
- 當m為質數的時候直接使用費馬小定理,m非質數使用歐拉函數。
- 當m為質數的時候,神奇的線性方法。
2.費馬小定理:假如a是一個整數,p是一個質數,那麽是a的倍數,可以表示為 如果a不是p的倍數,也可以寫成
3.拓展歐幾裏得:已知整數a,b,拓展歐幾裏得算法可以在求得a,b的最大公約數的同時,能找到整數x,y(其中一個很可能是負數),使它們滿足貝祖等式
4.分析乘法逆元:ax≡1 (mod p) 這個等式用中文描述就是 a乘一個數x並模p等於1,即 a%p * x%p = res, res % p=1;看上去就是同余定理的一個簡單等式 - -。那麽問題來了。
5.為什麽可以用擴展歐幾裏得求得逆元?我們知道模就是余數,比如12%5=12-5*2=2,18%4=18-4*4=2 那麽ax ≡ 1(mod p)即 ax - yp =1.把y寫成+的形式就是ax+py=1,為方便理解下面,我們把p寫成b就是ax + by=1。就表示x是a的模b乘法逆元,y是b的模a乘法逆元。然後就可以用歐幾裏得求。
6.乘法逆元有什麽用?做題時如果結果過大一般都會讓你模一個數,確保結果不是很大,而這個數一般是1e9+7,而且這個數又是個素數,加減乘與模運算的順序交換不會影響結果,但是除法不行。有的題目要求結果mod一個大質數,如果原本的結果中有除法,比如除以a,那就可以乘以a的逆元替代。
拓展歐幾裏得求逆元代碼:【時間復雜度為O(logn)】
#include<bits/stdc++.h> using namespace std; typedef long long ll; void exgcd(ll a,ll b,ll& d,ll& x,ll& y) { if(!b) { d = a; x = 1; y = 0; } else{ exgcd(b, a%b, d, y, x); y -= x*(a/b); } } ll inv(ll a, ll p) { ll d, x, y; exgcd(a, p, d, x, y); return d == 1 ? (x+p)%p : -1; } int main() { ll a,p; while(1) { scanf("%lld %lld",&a,&p); printf("%lld\n",inv(a,p)); } }拓展歐幾裏得
費馬小定理求逆元代碼:【O(log2N),在幾次測試中,常數似乎較上種方法大】
ll power_mod(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } inv2 = power_mod(a, mod - 2, mod);費馬小定理
當模p不是素數的時候需要用到歐拉定理
歐拉定理求逆元代碼:【O(√n),即求出單個歐拉函數的值】
int eurler_phi(int n) { int res = n; for(int i = 2; i * i <= n; i++){ if(n % i == 0){ res = res / i * (i - 1); while(n % i == 0) n /= i; } } if(n != 1) res = res / n * (n - 1); return res; }歐拉定理
參考:乘法逆元小結+ 逆元的幾種求法(擴展歐幾裏得,費馬小定理或歐拉定理,特例,打表等)+ ACM數論之旅6---數論倒數,又稱逆元(我整個人都倒了( ̄﹏ ̄))
乘法逆元模板