擴展歐幾里得求逆元


[牛客網]A Number Theoretical Problem

題目鏈接:https://ac.nowcoder.com/acm/problem/207599

 

 

這貌似是一道求逆元的模板題,但是。。。

逆元是什么啊!!!擴展歐幾里得是什么啊!!!

於是我今天花了一下去看別人的博客,一臉懵逼的進去,一臉懵逼的出來。

其實有很多地方我都搞不明白,但也管不了那么多啦,把我搞明白的講出來就可以了

 

首先我們都知道歐幾里得算法(也叫輾轉相除法)能求出兩個數a,b的最大公約數,即gcd(a,b)=gcd(b,a%b)。

復雜度是o(ln n)

代碼實現如下:

#include <iostream>
using namespace std;
int gcd(int a,int b);
int main(){
	int a,b;	
	scanf("%d %d",&a,&b);
	printf("%d %d\n",a,b);
	printf("gcd=%d\n",gcd(a,b));
} 
int gcd(int a,int b){
	if(b==0) return a;
	int ans=gcd(b,a%b);
	return ans;
}

 

再次基礎上的擴展歐幾里能快速求ax+by=gcd(a,b)一個二元一次方程的一個特解

當歐幾里得算法遞歸到最后一層,即b==0時,那么gcd=a,這就很容易得到一個特解 x=1,y=0,即a*x+b*y=a*1+b*0=a=gcd

得到特解x=1,y=0,在通過遞歸的返回,再來一層一層推出原本 ax+by=gck,的一組特解

關於如何推出上一層的特解在這里我們把上一層的解x,y。當前層已知的解為x1,y1

在這里的一個結論是 a%b=a-(a/b)*  (這里的 “/” 指的是整除,例如 5/2=2 , 1/3=0

由於上一層的gcd(a,b)等於當前層的gcd(b,a%b)

即gcd(b,a%b)=b*x1+(a%b)*y1

         =b*x1+(a-(a/b)*b)*y1

         =b*x1+  a*y1-(a/b)*b*y1

        =a*(y1)+b*(x1-(a/b)*y1)

 

又因為gcd(a,b)=a*x+b*y,即這兩式子相等,通過比較得   x=y1,y=x1-(a/b)*y1。

int gcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1,y=0;  \\  一組特解
		return a;
	}
	int ans=gcd(b,a%b,x,y);
	int t=x;        \\由最后一層的一組特解向上推
	x=y;    
	y=t-(a/b)*y;
	return ans;
}

 

如果只能擴展歐幾里快速求ax+by=gcd(a,b)一個二元一次方程的一個特解,可能會說:“ 就這???”

 

這里我們就要知道擴展歐幾里快速乘法逆元

 

然后就是逆元的定義

什么叫乘法逆元? 

a*x≡1(mod n)  (≡為同余符號,即(a*x)mod n==1 mod n)

這里,我們稱 x 是 a 關於 n 的乘法逆元

判斷逆元是否存在為gcd(a,n)是否等於1,等於即存在,否則不存在。

 

我們可以把他寫成一個表達式為:a*x+b*y=1(這里設b=n)

於是求逆元就要求這個二元一次方程組,則通過上面所說的擴展歐幾里得

但是有無數解,但一般會讓你求最小解x0,其實x0mod n就是最小解(這我真搞不明白)

考慮到x0可能是為負數,那我們先對其取模,x0mod n ,但x0還是負數,我們再加上其n,最后再取模。即 x0=(x0%n+n)%n

 

於是對於牛客上那道求逆元的模板題我們就迎刃而解了

代碼如下:

#include <iostream>
typedef long long LL;
using namespace std;
LL exgcd(LL a,LL b,LL &x,LL &y);
int t;
LL y,p;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%lld %lld",&y,&p);
		LL x=0,k=0;
		LL ok=exgcd(y,p,x,k);
		if(ok==1) printf("%lld\n",(x%p+p)%p);
		else printf("-1\n");
	}
} 
LL exgcd(LL a,LL b,LL &x,LL &y){
	if(b==0){
		x=1,y=0;
		return a;
	}
	int ans=exgcd(b,a%b,x,y);
	LL t=x;
	x=y;
	y=t-(a/b)*y;
	return ans;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM