逆元模板 與簡介
阿新 • • 發佈:2018-12-12
1.費馬小定理求逆元(求a對於mod的逆元,要求mod為素數)
由費馬小定理 a^(p-1)≡1 , 變形得 a*a^(p-2)≡1(mod p),答案已經很明顯了:若a,p互質,因為a*a^(p-2)≡1(mod p)且a*x≡1(mod p),則x=a^(p-2)(mod p),用快速冪可快速求之
- 複雜度O(logn);
- 適用範圍:一般在mod是個素數的時候用,比擴歐快一點而且好寫。
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);
2.擴充套件歐幾里得
給定模數m,求a的逆相當於求解ax=1(mod m) 這個方程可以轉化為ax-my=1 然後套用求二元一次方程的方法,用擴充套件歐幾里得演算法求得一組x0,y0和gcd 檢查gcd是否為1 gcd不為1則說明逆元不存在 若為1(a與m互質),則調整x0到0~m-1的範圍中即可
- PS:這種演算法效率較高,常數較小,時間複雜度為O(ln n)
- 適用範圍:只要存在逆元即可求,適用於個數不多但是mod很大的時候,也是最常見的一種求逆元的方法。
typedef long long ll; void extgcd(ll a,ll b,ll& d,ll& x,ll& y) { if(!b) { d=a; x=1; y=0;} else { extgcd(b,a%b,d,y,x); y-=x*(a/b); } } ll inverse(ll a,ll n) //求a在mod下的逆元,不存在逆元返回-1 { ll d,x,y; extgcd(a,n,d,x,y); return d==1?(x+n)%n:-1; //(x+n)%n 防止為負 }
3.逆元打表
有時會遇到這樣一種問題,在模質數p下,求1~n逆元 n< p(這裡為奇質數)。可以O(n)求出所有逆元,有一個遞推式如下
它的推導過程如下,設,那麼
對上式兩邊同時除,進一步得到
再把和替換掉,最終得到
初始化,這樣就可以通過遞推法求出1->n模奇素數的所有逆元了。
另外有個結論模的所有逆元值對應中所有的數,比如,那麼對應的逆元是。
線性求逆元
typedef long long ll; const int N = 1e5 + 5; int inv[N]; void inverse(int n, int p) { inv[1] = 1; for (int i=2; i<=n; ++i) { inv[i] = (ll) (p - p / i) * inv[p%i] % p; } }
注意:
- 呼叫前要先預處理
- 呼叫的時候要先對除數取mod
效能分析:
- 時間複雜度O(n)
- 適用範圍:p是不大的素數而且多次呼叫,比如盧卡斯定理。
遞迴求逆元
LL inv(LL i)
{
if(i==1)return 1;
return (mod-mod/i)*inv(mod%i)%mod;
}
效能分析
- 時間複雜度:O(logmod)
- 好像找到了最簡單的演算法了!!
- 適用範圍: mod數是素數,所以並不好用,比如中國剩餘定理中就不好使,因為很多時候可能會忘記考慮mod數是不是素數。
參考部落格