網上看了半天……還是沒把歐幾里得算法和擴展歐幾里得算法給弄明白……
然后想了想自己寫一篇文章好了……
參考文獻:https://www.cnblogs.com/hadilo/p/5914302.html
https://blog.csdn.net/sky_zdk/article/details/71023325
《算法競賽進階指南》(李煜東)(我不是來推銷的)
ps:本文討論范圍均在整數以內
一、歐幾里得算法
歐幾里得算法,即輾轉相除法,簡稱gcd,用於計算兩個數的最大公約數。時間復雜度據說log n的
公式 gcd(a,b)=gcd(b,a mod b) ,b≠0
啥?證明?能用就行要什么證明
咳咳,讓我們來嚴格的證明一下
證:
若a<b,以上等式顯然成立
若a>=b, 設a=q*b+r,0<=r<b, 即r=a%b 。對於a,b的任意公約數d,因為d|a(ps:這個符號的意思是d是a的約數),d|b,所以d|q*b,可得d|(a-q*b),即d|r,d也是r的約數。反之亦成立。所以,a,b的公約數集合和b,a mod b的公約數集合相同,他們的最大公約數自然也相同。
證畢
上代碼(我知道你們只想要這個)
int gcd(int a,int b){ return b?gcd(b,a%b):a; }
當然還有更裝逼別致的寫法
int gcd(int a,int b){ if(!b) return a; while(b^=a^=b^=a%=b);//先做一次取模,然后交換兩個元素 return a; }
ps:問題就是取模太慢了,有的題目可能用更相減損法還更快……所以更相減損大法好(口胡)<---別聽這家伙瞎說,時間復雜度根本不一樣
二、擴展歐幾里得算法
一個很厲害的東西,我看了半天沒弄明白(隊長好像一看就懂)
引理:裴蜀定理,一定存在ax+by=gcd(a,b)的整數解
亂證嚴格的證明:
當b=0時,gcd(a,b)=a,顯然存在一對整數解x=1,y=0
若b!=0
設ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2
又因 a%b=a-a/b*b
則 ax1+by1=bx2+(a-a/b*b)y2
ax1+by1=bx2+ay2-a/b*by2
ax1+by1=ay2+bx2-b*a/b*y2
ax1+by1=ay2+b(x2-a/b*y2)
解得 x1=y2 , y1=x2-a/b*y2
因為當 b=0 時存在 x , y 為最后一組解
而每一組的解可根據后一組得到
所以第一組的解 x , y 必然存在
證畢
然后遞歸計算就可以,當b=0時,返回x=1,y=0即可
上代碼
int exgcd(int a,int b,int &x,int &y){ //注意x和y必須是引用 if(!b){x=1,y=0;return a;} int d=exgcd(b,a%b,x,y); int t=x;x=y,y=t-(a/b)*y; return d;//d是a和b的gcd,順便求出來 }
san三、擴歐解不定方程
對於形如ax+by=c的方程
先用exgcd求出ax+by=gcd(a,b)的一組解x0,x1
根據裴蜀定理(上面那個),如果c%gcd(a,b)==0,此方程有解,否則無解
若有解
設d=gcd(a,b)
令方程兩邊同乘上c/d
則方程變為(a*c/d)x0+(b*c/d)y0=c
於是我們就得到了一組解了,x1=x0*c/d,y1=y0*c/d
然后就學不下去了orz……但問題是還沒完QAQ
題目里面,一般不可能讓你只求出一組解,一般都是要你求出最小整數解
保佑自己上面算出來的剛好是答案就好了,只要夠歐就沒問題
認真點
求最小整數解,就是把x1減小到不能減小為止,再看看上面的式子,c和d都是定值,於是我們只要考慮把x0減小到不能減小為止
設x0減小x,方程左邊總體減小x*c/d*a,為了保證等式成立,y0增加的值y應滿足x*c/d*a=y*c/d*b
於是x/y=(b/d) / ( a/d)
我們令x0不斷減x(x=b/d),直到0<=x1<x,此時的x1即為最小整數解
我們可以令x1=x0%x,就可以算出x1了
等等,真的大丈夫?
如果x0是負數怎么辦?我們讓x1+x就可以得到正數了
如果x是負數呢?令x=abs(x)即可
關於以上兩點,請自行考慮(才不是因為我也不會呢)
上代碼啦
d=exgcd(a,b,x,y); if(c%d==0) { x*=c/d; t=b/d; t=abs(t); x=(x%t+t)%t; printf("%d\n",x); }
四、解線性同余方程
這個隨便提一嘴吧,因為我也不太會
對於ax≡c(mod p)
將其轉化為ax+py=c
然后帶進上面求解,一般都是要求出x的最小整數解的
emm……差不多了,就這樣吧
以上