逆元的幾種求法(擴充套件歐幾里得,費馬小定理或尤拉定理,特例,打表等)
乘法逆元
對於縮系中的元素,每個數a均有唯一的與之對應的乘法逆元x,使得ax≡1(mod n)
一個數有逆元的充分必要條件是gcd(a,n)=1,此時逆元唯一存在
逆元的含義:模n意義下,1個數a如果有逆元x,那麼除以a相當於乘以x。
1.擴充套件歐幾里得
給定模數m,求a的逆相當於求解ax=1(mod m)
這個方程可以轉化為ax-my=1
然後套用求二元一次方程的方法,用擴充套件歐幾里得演算法求得一組x0,y0和gcd
檢查gcd是否為1
gcd不為1則說明逆元不存在
若為1,則調整x0到0~m-1的範圍中即可
PS:這種演算法效率較高,常數較小,時間複雜度為O(ln n)
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){
ll d,x,y;
extgcd(a,n,d,x,y);
return d==1?(x+n)%n:-1;
}
2.費馬小定理
在模為素數p的情況下,有費馬小定理
a^(p-1)=1(mod p)
那麼a^(p-2)=a^-1(mod p)
也就是說a的逆元為a^(p-2)
而在模不為素數p的情況下,有尤拉定理
a^phi(m)=1(mod m) (a⊥m)
同理a^-1=a^(phi(m)-1)
因此逆元x便可以套用快速冪求得了x=a^(phi(m)-1)
但是似乎還有個問題?如何判斷a是否有逆元呢?
檢驗逆元的性質,看求出的冪值x與a相乘是否為1即可
PS:這種演算法複雜度為O(log2N)在幾次測試中,常數似乎較上種方法大
當p比較大的時候需要用快速冪求解
typedef long long ll; ll pow_mod(ll x, ll n, ll mod){ ll res=1; while(n>0){ if(n&1)res=res*x%mod; x=x*x%mod; n>>=1; } return res; }
當模p不是素數的時候需要用到尤拉定理
a^phi(p)≡1 (mod p) a*a^(phi(p)-1)≡1 (mod p)a^(-1)≡a^(phi(p)-1) (mod p)
所以aϕ(m)−1為a的逆元
時間複雜度O(n√)即求出單個尤拉函式的值
(當p為素數的時候phi(p)=p-1,則phi(p)-1=p-2可以看出尤拉定理是費馬小定理的推廣) PS:這裡就貼出尤拉定理的板子,很少會用尤拉定理求逆元
3.特殊情況
一:
當N是質數,a是(N+1)的約數時,a−1=N+1a
這點也很好理解。當N是質數,0 < a < N時,(a,N)=1,則a肯定存在逆元。
而解出的N+1a就滿足N+1a⋅a≡1(modN),故它是a的逆元。
在CF 696C,N=1000000007時
2−1=1000000007+12=500000004 3−1=1000000007+13=333333336 求解就灰常方便了… 二: 求逆元一般公式(條件b|a)ans=a/bmodm=amod(mb)/b
公式證明:
PS:實際上a mod (bm)/b這種的對於所有的都適用,不區分互不互素,而費馬小定理和擴充套件歐幾里得演算法求逆元是有侷限性的,它們都會要求與互素,如果a與m不互素,那就沒有逆元,這個時候需要a mod (bm)/b來搞(此時就不是逆元的概念了)。但是當a與m互素的時候,bm可能會很大,不適合套這個一般公式,所以大部分時候還是用逆元來搞
4.逆元打表
有時會遇到這樣一種問題,在模質數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;
}
}
例題可以參考http://blog.csdn.net/acdreamers/article/details/8220787