求解最大公約數和最小公倍數。
有兩大種方法:求解最大公約數有歐幾裡德演算法和stein演算法。其中歐幾裡德演算法又分為一般的和擴充的歐幾裡德演算法。
而求解兩數的最小公倍數則利用了它們的最大公約數,有公式lcm(a,b)=a*b/gcd(a,b)。lcm是最小公倍數的英文縮寫,gcd同樣。
所以求解最小公倍數的代碼只在一般歐幾裡德演算法分析中介紹下,讀者可以舉一反三。
一、歐幾裡德演算法
1、一般歐幾裡德演算法(只求最大公約數)
求最大公約數一個經典的方法就是輾轉相除法了。以下將要介紹的代碼也是利用其求解的。
其中,求解最大公約數用了遞迴和非遞迴兩種方法,代碼中有注釋。
代碼如下:
#include<iostream>using namespace std;int gcd(int a,int b) //a,b最大公約數,遞迴 {if(b==0)return a;else return gcd(b,a%b);}int GCD(int a,int b) //a,b最大公約數,非遞迴 { int tem; if(a<b) { tem=a; a=b; b=tem; } int rem=1; while(rem!=0) { rem=a%b; a=b; b=rem; } return a;} int lcm(int a,int b) //最小公倍數 { return a*b/gcd(a,b);}int main(){ int a,b,lc,gc1,gc2; cout<<"輸入:"; while(cin>>a>>b) { gc1=gcd(a,b); gc2=GCD(a,b); lc=lcm(a,b); cout<<"最大公約數:"<<gc1<<endl; cout<<"最大公約數:"<<gc2<<endl; cout<<"最小公倍數:"<<lc<<endl; cout<<endl; cout<<"輸入:"; } return 0;}
測試結果:
再來囉嗦兩句。求解最大公約數的非遞迴方法應該說很好理解,完全的輾轉相除法。而遞迴方法則由於沒有判斷a,b的大小關係,很容易讓人迷惑。
比如,輸入28 1274時,會有人覺得不能求解。其實,輸入28 1274和輸入1274 28是基本一樣的。解釋如下:輸入28 1274,調用gcd(28,1274),
b=1274,b!=0,所以執行return gcd(b,a%b)也即return gcd(1274,28)。因為28%1274=28!到此,已經和直接輸入1274 28一樣了。
2、擴充歐幾裡德演算法(不僅僅可求出最大公約數,還可解出方程ax+by=gcd(a,b)中的x和y)
其詳細描述及證明,請參考:點擊開啟連結
演算法代碼:
int extendGcd(int a,int b,int &x,int &y) //擴充gcd,可以求出gcd(a,b)以及ax+by=gcd(a,b)中x,y的值{if(b==0){x=1;y=0;return a;}else{int a1=extendGcd(b,a%b,x,y);int tmp=x;x=y;y=tmp-a/b*y;return a1;}}
二、Stein演算法
Stein演算法與歐幾裡德演算法的不同是:Stein演算法只有整數的移位與加減法,不需要進行除法和模數運算。
演算法描述:
(1)判斷a或b是否為0,若a=0,則b就是最小公倍數;若b=0,則a就是最小公倍數。演算法結束。
(2)設a1=a,b1=b,c1=1。
(3)判斷an、bn是否為偶數
①若都是偶數,則令a(n+1)=an/2,b(n+1)=bn/2,c(n+1)=2*cn。
②若an是偶數,則令a(n+1)=an/2,b(n+1)=bn,c(n+1)=cn。
③若bn是偶數,則令a(n+1)=an,b(n+1)=bn/2,c(n+1)=cn。
④若an、bn都是奇數,則令a(n+1)=|an-bn|,b(n+1)=min(an,bn),c(n+1)=cn。
(4)n累加1,跳轉到(3)進行下一輪計算。
演算法代碼如下:
方法一:
int gcd(int a,int b){int min=(a>b)?b:a;int max=(a>b)?a:b;if(min==0)return max;if(max%2==0 && min%2==0)return 2*gcd(max/2,min/2);if(max%2==0)return gcd(max/2,min);if(min%2==0)return gcd(max,min/2);return gcd((max+min)/2,(max-min)/2); //都是奇數,貌似和演算法給出的不太一樣,我測試了幾組資料都是對的。。。}
方法二:
int gcd(int a,int b){int min=(a>b)?b:a;int max=(a>b)?a:b;if(!min)return max;if(!max)return min;if(!(min&1) && !(max&1)) //都是偶數return gcd(min>>1,max>>1)<<1;if(!(min&1)) //小數為偶return gcd(min>>1,max);if(!(max&1)) //大數為偶return gcd(min,max>>1);//return gcd((max-min)>>1,min); //都是奇數,不知道為啥這個也行??? return gcd(max-min,min); //都是奇數}