乘法逆元,一般是用來求
的值,p通常為質數
定義
若a*x≡1(mod b),且a與b互質,我們定義x是a的逆元,記為a^(-1),所以也可以說x是a在mod b意義下的倒數
所以對於a/b(mod p),我們可以先求出b在mod p下的逆元,然后乘a再mod p就是這個分數的值了
逆元求法
首先看到同余方程,這個就是典型的求一個數模p下的逆元,而對於逆元的求法,我們有多種操作:
擴展歐幾里得
首先,這個算法的性質如下
擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式: ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。擴展歐幾里德常用在求解模線性方程及方程組中。
在這道題中,我們可以把ax≡1(mod b)轉化成ax+by=1,只不過y可能是負數,然而與擴歐公式還是有差別,但不難得出,gcd(a,b)=1,
推導公式如下
由最大公因數的定義,可知 a 是 gcd(a,b) 的倍數,且 b 是 gcd(a,b) 的倍數, 若 x,y 都是整數,就確定了 ax + by 是 gcd(a,b) 的倍數, 因為 m = ax + by所以 m 必須是 gcd(a,b) 的倍數, 那么 m \mod gcd(a,b) = 0
..................................................
然后根據一系列推導就得出了具體公式:具體請見同余方程第一篇題解<<<<大佬。
1 #include<bits/stdc++.h> 2 using namespace std; 3 long long x,y; 4 void exgcd(long long a,long long b) 5 { 6 if(b==0) 7 { 8 x=1; 9 y=0; 10 return; 11 } 12 exgcd(b,a%b); 13 long long z=x; 14 x=y; 15 y=z-(a/b)*y; 16 } 17 int main() 18 { 19 long long a,b; 20 cin>>a>>b; 21 exgcd(a,b); 22 while(x<0) 23 x+=b; 24 x%=b; 25 cout<<x; 26 return 0; 27 }
快速冪
這個做法運用到了費馬小定理
若p為素數,a為正整數,且a、p互質。 則有a^(p-1)≡1(mod p)。
然后代入原式,神奇的事發生了
a*x≡1(mod p) a*x=a^(p-1) (mod p) x=a^(p-2) (mod p)
然后我們求a^(p-2)(mod p)就是它的逆元啦
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 ll n,p; 5 int fpm(ll x,ll y)//快速冪 6 { 7 x%=p; 8 ll ans=1; 9 while(y) 10 { 11 if(y&1)ans=(ans*x)%p; 12 y>>=1; 13 x=x*x%p; 14 } 15 return ans; 16 } 17 int main() 18 { 19 cin>>n>>p; 20 for(int i=1;i<=n;i++) 21 { 22 printf("%lld\n",fpm(i,p-2)); 23 } 24 }
線性算法
以上算法針對於求單個逆元,但是有一長串的時候,你就TLE了,所以,聰明的大佬們研發的線性算法出現了。
設x的逆元為x^(-1)
我們先有一個1的逆元為1
設p=k*i+r,(1<r<i<p) 也就是 k 是 p / i的商,r是余數 。
然后乘上i的逆元和r的逆元
然后公式就出來了
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 ll n,p; 5 ll inv[3000005]; 6 int main() 7 { 8 cin>>n>>p; 9 inv[1]=1; 10 printf("%lld\n",inv[1]); 11 for(int i=2;i<=n;i++) 12 { 13 inv[i]=(p-p/i)*inv[p%i]%p; 14 printf("%lld\n",inv[i]); 15 } 16 }
學會了求逆元后,我們就可以學學其他interesting的東西——中國剩余定理