來源:http://www.cnblogs.com/void/archive/2011/04/18/2020357.html
剛開始接觸歐幾里德解不定方程的時候,對於方程 ax + by = gcd(a, b)一定有解很糾結,非常糾結,它爲什麼就一定有解,爲什麼??!!
後來看了好些個(對,就是很多)博客,也就慢慢釋然了,人家說有解就有解了,不服你就買本數論去學呀,學個幾十年搞懂了再回來敲代碼啊,人家數學家早那麼多年都證明出來了,已經肯定有解了,你還糾結什麼?腦殘。。。。吐槽完畢;
後來看解不定方程有的地方還是不太理解,看來這段博客的最後一段,就有了一種通透的感覺,放在這裏方便以後在看,也分享給大家,多謝原博主,謝謝!
擴展歐幾里德算法是用來在已知a, b求解一組p,q使得p * a+q * b = Gcd(a, b) (解一定存在,根據數論中的相關定理)。擴展歐幾里德常用在求解模線性方程及方程組中。下面是一個使用C++的實現:
int exGcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
int r = exGcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return r;
}
把這個實現和Gcd的遞歸實現相比,發現多了下面的x,y賦值過程,這就是擴展歐幾里德算法的精髓。
可以這樣思考:
對於a' = b, b' = a % b 而言,我們求得 x, y使得 a'x + b'y = Gcd(a', b')
由於b' = a % b = a - a / b * b (注:這裏的/是程序設計語言中的除法)
那麼可以得到:
a'x + b'y = Gcd(a', b') ===>
bx + (a - a / b * b)y = Gcd(a', b') = Gcd(a, b) ===>
ay +b(x - a / b*y) = Gcd(a, b)
因此對於a和b而言,他們的相對應的p,q分別是 y和(x-a/b*y)
補充:關於使用擴展歐幾里德算法解決不定方程的辦法
對於不定整數方程pa+qb=c,若 c mod Gcd(p, q)=0,則該方程存在整數解,否則不存在整數解。
上面已經列出找一個整數解的方法,在找到p * a+q * b = Gcd(p, q)的一組解p0,q0後,p * a+q * b = Gcd(p, q)的其他整數解滿足:
p = p0 + b/Gcd(p, q) * t
q = q0 - a/Gcd(p, q) * t(其中t爲任意整數)
至於pa+qb=c的整數解,只需將p * a+q * b = Gcd(p, q)的每個解乘上 c/Gcd(p, q) 即可。
轉: 首先擴展歐幾里德主要是用來與求解線性方程相關的問題,所以我們從一個線性方程開始分析。現在假設這個線性方程爲a*x+b*y=m,如果這個線性方程有解,那麼一定有gcd(a,b) | m,即a,b的最大公約數能夠整除m(m%gcd(a,b)==0)。證明很簡單,由於a%gcd(a,b)==b%gcd(a,b)==0,所以a*x+b*y肯定能夠整除gcd(a,b),如果線性方程成立,那麼就可以用m代替a*x+b*y,從而得到上面的結論,利用上面的結論就可以用來判斷一個線性方程是否有解。 那麼在a*x+b*y=m這個線性方程成立的情況下,如何來求解x和y呢? 1.令a1=a/gcd(a,b),b1=b/gcd(a,b),m1=m/gcd(a,b)。如果我們能夠首先求出滿足a*x1+b*y1=gcd(a,b)這個方程的x1和y1,那麼x=x1*m1,y=y1*m1就可以求出來了。由歐幾里德算法gcd(a,b)=gcd(b,a%b),所以a*x1+b*y1=gcd(a,b)=gcd(b,a%b)=b*x2+(a%b)*y2,現在只要做一些變形就可以得到擴展歐幾里德算法中的用到的式子了。令k=a/b(商),r=a%b(餘數),那麼a=k*b+r。所以r=a-k*b,帶入上式,得到a*x1+b*y1=b*x2+(a-(a/b)*b)y2=a*y2+b*(x2-(a/b)*y2) => x1=y2,y1=x2-(a/b)*y2。有了這兩個式子我們就知道了在用歐幾里德求最大公約數的時候,相應的參數x,y的變化。現在再回過頭來看一下擴展歐幾里德算法的代碼就很好理解了,實際上擴展歐幾里德就是在求a和b的最大公約數的同時,也將滿足方程a*x1+b*y1=gcd(a,b)的一組x1和y1的值求了出來。下面代碼中突出的部分就是標準的歐幾里德算法的代碼。 __int64 exGcd( __int64 a, __int64 b, __int64 &x, __int64 &y){ if (b==0){ x=1; y=0; return a; } __int64 g=exGcd(b,a%b,x,y); __int64 temp=x; x=y;//當爲 ax - by = gcd(a, b)時,此處爲 x = -y; 下面爲 y = -(t + (a/b)*y); 只需要此處改一下就行,主函數中原來對x , y怎麼處理還怎麼處理。 y=temp-(a/b)*y; return g; } 2.那麼x,y的一組解就是x1*m1,y1*m1,但是由於滿足方程的解無窮多個,在實際的解題中一般都會去求解x或是y的最小正數的值。以求x爲例,又該如何求解呢?還是從方程入手,現在的x,y已經滿足a*x+b*y=m,那麼a*(x+n*b)+b*(y-n*a)=m顯然也是成立的。可以得出x+n*b(n=…,-2,-1,0,1,2,…)就是方程的所有x解的集合,由於每一個x都肯定有一個y和其對應,所以在求解x的時候可以不考慮y的取值。取k使得x+k*b>0,x的最小正數值就應該是(x+k*b)%b,但是這個值真的是最小的嗎??如果我們將方程最有兩邊同時除以gcd(a,b),則方程變爲a1*x+b1*y=m1,同上面的分析可知,此時的最小值應該爲(x+k*b1)%b1,由於b1<=b,所以這個值一定會小於等於之前的值。在實際的求解過程中一般都是用 while (x<0)x+=b1來使得爲正的條件滿足,爲了更快的退出循環,可以將b1改爲b(b是b1的倍數),並將b乘以一個倍數後再加到x上。 |