1. 程式人生 > >51nod 1256 乘法逆元(擴充套件歐幾里得演算法)

51nod 1256 乘法逆元(擴充套件歐幾里得演算法)

思路1:把k*M%N=1可以寫成一個不定方程,(k*M)%N=(N*x+1)%N,那麼就是求k*M-N*x=1使得k最小,不定方程利用擴充套件歐幾里得演算法

--------------------------------------------------------------------------------------------------------------------------------

知識點:

  1. 擴充套件歐幾里德演算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式: ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。擴充套件歐幾里德常用在求解模線性方程及方程組中。

2、ax+by=gcd(a,b);

設a>b

b=0時,gcd(a,b)=a.此時,x=1,y=0;

a>b>0,①:ax1+by1=gcd(a,b);②:bx2+(a mod b)y2=gcd( b , a mod b );第二個式子利用gcd的輾轉相除法演算法得到(gcd(a,b)=gcd(b,a mod b))。

然後將gcd左邊兩個等式列個等式:ax1+by1=bx2+(a mod b)y2;

a mod b可以寫成a-(a/b)b,那麼等式變成ax1+ by1= bx2+ (a - (a / b) * b)y2=bx2+ay2 - (a / b)by2 ;把ax1+ by1=bx2+ay2 - (a / b)by2拎出來,整理一下,寫成:ax1+by1=ay2+b(x2-(a/b)y2); 可以得到,x1=y2,y1=x2-(a/b)y2;

這樣我們就得到了求解 x1,y1 的方法:x1,y1 的值基於 x2,y2.對於演算法裡面x,y的由來我們這裡就知道啦

3、擴充套件歐幾里得演算法用於除法取模(求逆元),關鍵是要求b、p互質( gcd(b, p) == 1)

設k為b的乘法逆元,則在求解除法取模問題時:

有(a/b)%p =>(a*k)%p

乘法逆元:

如果b*k ≡ 1 (mod p)即b*k%p=1,則稱k是b關於p的乘法逆元

我們可以通過求 b 關於 p 的乘法逆元 k,將 a 乘上 k 再模 p,即 (a * k) mod p。其結果與(a / b) mod p等價。

證: 

因為 b * k ≡ 1 (mod p) 

則有 b * k = p* x+1 

得到 k = (p * x + 1) / b 

將 k 代入(a * k) mod p 

得到: 

(a * (p * x + 1) / b) mod p 

=((a * p * x) / b + a / b) mod p 

=[((a * p * x) / b) mod p +(a / b)] mod p 

=[(p * (a * x) / b) mod p +(a / b)] mod p 

=(0 + (a / b)) mod p 

= (a/b) mod p

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int ,int > P;
#define  Max  int(5e2+10)
#define INF 0xf3f3f3f
int n,m;
void Euild(int a, int b, int &x, int &y) { // x 是 a 關於 b 的乘法逆元
	if(0 == b) {
		x = 1, y = 0;
		return ;
	}
	Euild(b, a%b, x, y);
	int flag = x;
	x = y;
	y = flag - a/b * y;

}
int main() {
	scanf("%d%d",&m,&n);
	int x,y;
    Euild(m,-n,x,y);
    while(x<0)
    	x+=n;
	
    printf("%d\n",x);
}

思路2:

費馬小定理表明一個數a的乘法逆元(關於m取模)等於a^(phi(m)-1).

phi(m)為m的尤拉函式值,顯然當m為素數時phi(m)=m-1,即此時的乘法逆元為a^(m-2).

----------------------------------------------------------------------------------------------------------------------------

#include <iostream>

#include <cmath>

using namespace std;

typedef long long ll;

ll n;//模

ll m;

ll pow_mod(ll x,ll i)

{

    ll ans=1;

    while(i)

    {

        if(i&1)

            ans=(ans*x)%n;

        i>>=1;

        x=(x*x)%n;

    }

    return ans;

}

ll eular(ll a)

{

    ll ans=a;

    ll tmp=sqrt(a+0.5);

    for(ll i=2;i<=tmp;i++)

    {

        if(a==1)

            break;

        if(a%i==0)

        {

            ans=ans/i*(i-1);

            while(a%i==0)

                a/=i;

        }

    }

    if(a!=1)

        ans=ans/a*(a-1);

    return ans;

}

int main()

{

    ios::sync_with_stdio(false);

    while(cin>>m>>n)

    {

        cout<<pow_mod(m,eular(n)-1)%n<<endl;

    }

    return 0;

}