轉載地址:http://www.cnblogs.com/dupengcheng/p/5487362.html
在開始之前我們先介紹3個定理:
1.乘法逆元(在維基百科中也叫倒數,當然是 mod p後的,其實就是倒數不是嗎?):
如果ax≡1 (mod p),且gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元爲x。
2.費馬小定理(定義來自維基百科):
假如a是一個整數,p是一個質數,那麼是p的倍數,可以表示爲
如果a不是p的倍數,這個定理也可以寫成
3.
擴展歐幾里得
(定義來自維基百科):
已知整數a、b,擴展歐幾里得算法可以在求得a、b的最大公約數的同時,能找到整數x、y(其中一個很可能是負數),使它們滿足貝祖等式。
好了,在明白上面的定理後我們開始分析乘法逆元:ax≡1 (mod p) 這個等式用中文描述就是 a乘一個數x並模p等於1,即 a%p*x%p=res,res%p=1;看上去就是同餘定理的一個簡單等式- -。那麼問題來了。
爲什麼可以用費馬小定理來求逆元呢?
由費馬小定理 ap-1≡1 , 變形得 a*ap-2≡1(mod p),答案已經很明顯了:若a,p互質,因爲a*ap-2≡1(mod p)且a*x≡1(mod p),則x=ap-2(mod p),用快速冪可快速求之。
爲什麼可以用擴展歐幾里得求得逆元?
我們都知道模就是餘數,比如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乘法逆元。然後就可以用擴展歐幾里得求了。
知道逆元怎麼算之後,那麼乘法逆元有什麼用呢?
做題時如果結果過大一般都會讓你模一個數,確保結果不是很大,而這個數一般是1e9+7,而且這個數又是個素數,加減乘與模運算的順序交換不會影響結果,但是除法不行。有的題目要求結果mod一個大質數,如果原本的結果中有除法,比如除以a,那就可以乘以a的逆元替代。(除一個數等於乘它的倒數,雖然這裏的逆元不完全是倒數,但可以這麼理解,畢竟乘法逆元就是倒數的擴展)。
擴展歐幾里得求逆元代碼:
#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));
}
}