1. 程式人生 > >拓展歐幾里得小結(轉載)

拓展歐幾里得小結(轉載)

什麼是拓展歐幾里得?簡單的說,就是求關於x,y的方程 ax + by = gcd(a,b) 的所有整數解

現在我們令g = gcd(a,b)
則方程變成了ax + by = g
假如我們現在知道了關於這個方程的一個特解x0, y0,我們就可以用一種方法求出所有的整數解。
說的比較模糊,現在整理一下。
上面提到了兩個問題
一、怎麼求出這個特解?
二、怎麼由特解推出其它的所有解?

一、求特解
我們知道,歐幾里得公式可以由這個式子表示:
gcd(a,b) = gcd(b, a%b)
不斷往下連等,直到b = 0,此時a即為最大公約數
那麼我們來討論另一個問題,下面兩個式子有沒有關係呢?

a * x1 + b * y1 = g(a,b)
b * x2 + (a%b) * y2 = g(b,a%b)

如果看這篇部落格的你還沒接觸過拓展歐幾里得,那麼可能你還不知道現在在幹什麼。
那麼,我可以告訴你,只要找出x1和x2的關係、y1和y2的關係,我們就能求出方程a * x + b * y = g的一個特解

回到剛才那個問題,很顯然,兩個等式右邊就是歐幾里得的公式
那麼我們就能得出
a * x1 + b * y1 = b * x2 + (a%b) * y2
其中a%b可以換成a-(a/b)*b
式子變成了
a * x1 + b * y1 = b * x2 + (a - (a / b) * b) * y2
因為我們要找x1 y1和x2 y2的關係
我們可以用待定係數法,按照這種方法把右邊化成b * (x2 - (a / b) * y2) + a * y2
則等式變成了
a * x1 + b * y1 = a * y2 + b * (x2 - (a / b) * y2)
(上面的過程最好動手演算)
好,我們現在得出了下面兩個等式:
x1 = y2(等式兩邊a的係數相同)
y1 = x2 - (a / b) * y2 (等式兩邊b的係數相同)
也就是說,我們知道了方程b * x2 + (a%b) * y2 = g(b,a%b) 的解x2, y2,就可以得到方程a * x1 + b * y1 = g(a,b) 的解x1,y1了,這個小問題告一段落。

對於方程a * x + b * y = gcd(a,b),我們可以不斷的往下變成b * x + (a % b) y = gcd(a,b) ,按照歐幾里得的過程,b會變成0,即此時方程a * x + b * y = gcd(a,b) 因為b = 0,變成了a * x = gcd(a,b) ,還是由歐幾里得演算法得到,此時a就等於gcd(a,b),所以得到由原方程往下推了不知道多少次的方程 a * x = gcd(a,b) 來說,得到一個解x = 1, y = 0。現在得到了這個方程的解,再回溯回去,就可以得出原方程 a * x + b * y = gcd(a,b) 的一個特解。

OK,這個問題解決了。

二、怎麼利用特解推出其他所有整數解?
先說結論:
對於關於x,y的方程a * x + b * y = g 來說,讓x增加b/g,讓y減少a/g,等式兩邊還相等。(注意一個增加一個減少)
為什麼呢?
這個很容易得到,我們算一下即可。
讓x增加b/g,對於a * x這一項來說,增加了a * b / g,可以看出這是a,b的最小公倍數
同樣,對於b * y這一項來說,y 減少 a/b,這一項增加了a * b / g,同樣是a,b的最小公倍數。
可以看出,這兩項一項增加了一個最小公倍數,一項減少了一個最小公倍數,加起來的和仍然等於g。
這樣我們就明白了,其實這種操作就是增加一個最小公倍數,減少一個最小公倍數,這樣來改變x,y的值,來求出所有x,y的通解的。

那為什麼改變的是最小公倍數而不是更小的數呢?因為是 最小 公倍數呀…不會再找到更小的值了,這個自行體會吧…

到這裡為止,開頭提出的兩個問題都已經解決。
也就是說,對於方程a * x + b * y = gcd(a,b),我們能找出所有符合條件的解了。而且,還可以告訴你,這個方程是一定有無數個解的。

那麼來一個更一般的問題,對於方程a * x + b * y = c 來說,它的解怎麼求呢?
答:如果c % gcd(a,b) != 0,即c不是gcd的整數倍,則無解。
如果c % gcd(a,b) == 0 且 c / gcd(a,b) = t,那麼求出方程 a * x + b * y = gcd(a,b)的所有解x,y,將x,y乘上t,對應的x’,y’即是方程a * x + b * y = t * gcd(a,b)的解
很好理解,就不解釋了,仔細看上面的這段話就能理解

附上拓展歐幾里得的程式碼
 

void e_gcd(int a, int b, int &gcd, int &x, int &y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		gcd = a;
	}
	else
	{
		e_gcd(b, a % b, gcd, y, x);
		y -= x * (a / b)
	}
}