關於擴展歐幾里得算法(Extended Euclidean Algorithm),我是在做青蛙的約會這一經典題目才接觸到這個算法的。后面也有關於這一題的AC代碼和解題思路。
內容:已知a, b,求解一組x,y,使它們滿足貝祖等式: ax+by =gcd(a, b)
擴展歐幾里得算法,就和它的名字一樣是對歐幾里得算法的擴展。何為擴展?一是,該算法保留了歐幾里得算法的本質,可以求a與b的最大公約數。二是,已知a, b求解二元一次方程ax+by =gcd(a, b)的一組解(x,y)。
證明:
假設 a>b,
(1) b=0 gcd(a,b) = a , ax = a , 則x=1,y=0;(這里我還是推薦不把gcd(a,0)理解成最大公約數,而是一個計算機求出來的值)
(2) 假設 ax1+by1=gcd(a,b) (方程一) bx2+(a%b)y2=gcd(b,a%b)(方程二);由歐幾里得算法gcd(a,b) =gcd(b,a%b) 得到,
ax1+by1 = bx2+(a%b)y2,即ax1+by1=bx2+(a-a/b*b)y2 ax1+by1=ay2+b(x2-a/b*y2)
在根據多項式恆等定理(把a,b看成變量),x1=y2; y1=x2-a/b*y2;
(表面上看,就是已知方程一的一組解,可以得到方程二的一組解,已知方程二的一組解,就可以得到方程一的一組解,但是實際情況是,不可能先知道方程一的解(x1,y1)。)上述思想是遞歸定義的,不斷地利用gcd(a,b) =gcd(b,a%b),到b=0(y的系數為0)時,由(1)的解,根據解之間的關系,最終可以得到方程ax+by =gcd(a, b)的解。
遞歸形式代碼:
1 #include<iostream> 2 using namespace std; 3 4 int exgcd(int a,int b,int &x,int &y) 5 { 6 if(b==0) 7 { 8 x=1; 9 y=0; 10 return a; 11 } 12 int gcd=exgcd(b,a%b,x,y); 13 int x2=x,y2=y; 14 x=y2; 15 y=x2-(a/b)*y2; 16 return gcd; 17 } 18 19 int main() 20 { 21 int x,y,a,b; 22 cout<<"請輸入a和b:"<<endl; 23 cin>>a>>b; 24 cout<<"a和b的最大公約數:"<<endl; 25 cout<<exgcd(a,b,x,y)<<endl; 26 cout<<"ax+by=gcd(a,b) 的一組解是:"<<endl; 27 cout<<x<<" "<<y<<endl; 28 return 0; 29 }
非遞歸形式代碼:
1 #include<iostream> 2 using namespace std; 3 4 int exgcd(int a,int b,int &x,int &y) 5 { 6 int x1,y1,x0,y0; 7 x0=1; y0=0; 8 x1=0; y1=1; 9 x=0; y=1; 10 int r=a%b; 11 int q=(a-r)/b; 12 while(r) 13 { 14 x=x0-q*x1; y=y0-q*y1; 15 x0=x1; y0=y1; 16 x1=x; y1=y; 17 a=b; b=r; r=a%b; 18 q=(a-r)/b; 19 } 20 return b; 21 } 22 23 int main() 24 { 25 int x,y,a,b; 26 cout<<"請輸入a和b:"<<endl; 27 cin>>a>>b; 28 cout<<"a和b的最大公約數:"<<endl; 29 cout<<exgcd(a,b,x,y)<<endl; 30 cout<<"ax+by=gcd(a,b) 的一組解是:"<<endl; 31 cout<<x<<" "<<y<<endl; 32 return 0; 33 }
運行截圖:
同樣有兩點想說明:
1.擴展歐幾里得算法是對歐幾里得算法的擴展,可以求出gcd(a,b),好多人都沒意識到這一點。
2.x,y可以用全局變量,參數傳遞就不用傳引用了。