提到擴展歐幾里德算法,先簡要介紹下歐幾里德算法,又稱展轉相除法,用於計算兩個整數a和b的最大公約數(Greatest Common Divisor(GCD))。算法
爲證實gcd(a,b)=gcd(b,a mod b),只需證實
(1)gcd(a,b) | gcd(b,a mod b)
設d=gcd(a,b) 則d|a且d|b
a mod b=a-qb (設q=[a/b])
由d|ax+by 則d|a mod b
因此d| gcd(b,a mod b)
(2)gcd(b,a mod b) | gcd(a,b)
設d=gcd(b,a mod b)
則d|b且d|a mod b
a=qb+(a mod b) (q=[a/b])
得d|a
因此d|gcd(a mod b)ide
求解a和b的最大公約數中,a能夠表示爲kb+r,則r =a mod b,假設d是a和b的一個公約數,則有d|a,d|b,r = a - kb,所以d|r(由於d是a的約數,又是b的約數,因此也是他們的多項式的約數),因此d是(b,a mod b)的公約數。因此在這不斷代換中,a 必然>b,每次將a換成b,b換成amod b, a ,b 不斷減少。直到b爲0時,此時a爲他們的最大公約數。oop
1 int euclid(int a,int b) 2 { 3 if (b==0) 4 return a; 5 else 6 return euclid(b,a%b); 7 }
而擴展歐幾里德算法是用來求解已知a,b,求一組x,y使得a * x + b * y =Gcd(a,b)(這個解必定存在,數論定理)spa
由於Gcd(a,b)=Gcd(b,a%b),因此a * x + b * y = Gcd(a,b) = Gcd(b,a%b)。得b * x + (a%b)*y = b * x + (a - a/b*b)*y,這裏的((a - a/b*b)*y)不可貴出,以前是a%b。如今我想獲得這個數,怎麼來呢。首先a/b,獲得一個整數,在a!=b的狀況下,這個整數再乘以b以後是不等於a的。因此餘數就是這裏的差值。即a - a/b*b。若是a==b,那麼Gcd==a==b。繼續,b * x + (a%b)*y = b * x + (a - a/b*b)*y。展開得b*x + a*y - a/b*b*y,合併得a*y + b*(x-a/b*y).這樣,由於a,b都在減少,當b=0時,能夠得出x=1,y=0,此時遞歸回去能夠求出x,y。code
1 int extended_gcd(int a, int b, int &x, int &y) 2 { 3 int ret, tmp; 4 if (!b) 5 { 6 x = 1; 7 y = 0; 8 return a; 9 } 10 ret = extended_gcd(b, a % b,x, y); 11 tmp = x; 12 x = y; 13 y = tmp - a / b * y; 14 return ret; 15 } 16
對於方程a * x + b * y=c,該方程等價於a*c ≡ c(mod b);有整數解的充要條件是 c % gcd(a,b)==0。(定理)blog
因此方程 a*x+b*y=n;咱們能夠先用擴展歐幾里德算法求出一組x0,y0。即 a * x0 + b * y0 = Gcd(a,b);遞歸
而後兩邊同時除以Gcd(a,b) ,再乘以n。這樣就獲得了方程 :a * x0 * n / Gcd(a,b) + b * y0 * n / Gcd(a,b) =n;從而求出一個解get
經過擴展歐幾里德求的是ax + by = Gcd(a, b)的解,令解爲(x0, y0),代入原方程,得:ax0 + by0 = Gcd(a, b),
若是要求ax + by = c = Gcd(a, b)*c',能夠將上式代入,得:
ax + by = c = (ax0 + by0)c',則x = x0c', y = y0c', c' = c / Gcd(a,b)
這裏的(x, y)只是這個方程的其中一組解,x的通解爲 { x0c' + k*b/Gcd(a, b) | k爲任意整數 },y的通解能夠經過x通解的代入得出io
若gcd(a,b)=1,即a、b互質,且x0,y0爲a*x+b*y=n的一組解,則該方程的任一解可表示爲:x=x0+b*t,y=y0-a*t;且對任一整數t,皆成立。
這樣咱們就能夠求出方程的全部解了,但實際問題中,咱們每每被要求去求最小整數解,因此咱們就能夠將一個特解x。令t=b/gcd(a,b)=1,特解x=(x%t+t)%t;event
poj題目連接: