乘法逆元的求解

轉載地址: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));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章