擴展歐幾裏得算法詳解
阿新 • • 發佈:2018-10-19
rac int turn 發現 return 新解 求解 inline cda
我們觀察\(b*x_2+(a%b)*y_2\)這個式子,我們可以將\((a\%b)\)寫作\((a-\lfloor\frac{a}{b}\rfloor*b)\),將括號打開常數\(a,b\)合並,合並之後的結果為\(a*y_2+b*(x_2-\lfloor\frac{a}{b}\rfloor*y_2))\)
方程\(a*x+b*y=gcd(a,b)\)中,如果將x加上一個常數\(k_1,y\)減去一個常數\(k_2\),仍然保持原方程成立,那麽\(x+k_1,y-k-2\)
那我們應該加上什麽值才行呢?我們發現當\(a*k_1==b*k_2=t*lcm(a,b)\)可以保證得到所有解,於是每次尋找解就可以分別在\(x\)加上\(\frac{b}{gcd(a,b)},在y減去\frac{a}{gcd(a,b)}\)
本篇將附上擴展歐幾裏得算法的思想與推導;
對於一個方程\(a*x+b*y=gcd(a,b)\)來說,我們可以做如下的推導:
設有\(a*x_1+b*y_1=gcd(a,b)\);
同時我們有\(b*x_2+(a\%b)*y_2=gcd(b,a\%b)\);
對於這個方程組,我們希望知道的是\(x_1,x_2,y_1,y_2\)之間的關系,這樣我們才可以遞歸解決這個問題;
我們觀察\(b*x_2+(a%b)*y_2\)這個式子,我們可以將\((a\%b)\)寫作\((a-\lfloor\frac{a}{b}\rfloor*b)\),將括號打開常數\(a,b\)合並,合並之後的結果為\(a*y_2+b*(x_2-\lfloor\frac{a}{b}\rfloor*y_2))\)
由於歐幾裏得算法的原理\(gcd(a,b)==gcd(b,a%b)\),我們將兩式子聯立,對比系數即可得到\(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor*y_2\);
這個遞歸的邊界是什麽呢?我們知道,當樸素歐幾裏得到達邊界時,\(return gcd(a,0)=a\),那麽邊界條件就是對\(a*x_0+b*y_0=a\)求解,很顯然,此時\(x_0=1,y_0=0\);
當我們遞歸求出了一個方程的特解時,如何求出這個方程的通解呢?
方程\(a*x+b*y=gcd(a,b)\)中,如果將x加上一個常數\(k_1,y\)減去一個常數\(k_2\),仍然保持原方程成立,那麽\(x+k_1,y-k-2\) 就是方程的一個新解,這個k應該如何選擇呢?
實際上很簡單,\(a*(x+k_1)+b*(y+k_2)=gcd(a,b)\),打開括號,\(a*x+a*k_1+b*y-b*k_2=gcd(a,b)\),我們保證原方程成立,就需要\(a*k_1==b*k_2\),那麽顯然\(k_1=b,k_2=a\)是一種合理的情況,但是這樣是無法包含所有整數解的,因為我們加上的這個值並非是最小值.
那我們應該加上什麽值才行呢?我們發現當\(a*k_1==b*k_2=t*lcm(a,b)\)可以保證得到所有解,於是每次尋找解就可以分別在\(x\)加上\(\frac{b}{gcd(a,b)},在y減去\frac{a}{gcd(a,b)}\) 就可以了
對於方程\(a*x+b*y=c\)我們又該如何求解?我們發現如果\((c%gcd(a,b)!=0)\)那麽這個方程是無解的,而如果\(gcd(a,b)*t==c\),我們就可以按上面的方法求解之後對我們的解乘上一個\(t(t=\frac{c}{gcd(a,b)})\)
\(code:\)
int exgcd(int a,int b,int &x1,int &y1)
{
if(!b)
{
x1=1,y1=0;
return a;
}
int x2,y2;
int d=exgcd(b,a%b,x2,y2);
x1=y2,y1=x2-(a/b)*y2;
return d;
}
擴展歐幾裏得算法詳解