51nod 1256 乘法逆元(擴充套件歐幾里得演算法)
思路1:把k*M%N=1可以寫成一個不定方程,(k*M)%N=(N*x+1)%N,那麼就是求k*M-N*x=1使得k最小,不定方程利用擴充套件歐幾里得演算法
--------------------------------------------------------------------------------------------------------------------------------
知識點:
- 擴充套件歐幾里德演算法是用來在已知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;
}