轉載請註明出處:http://blog.csdn.net/ns_code/article/details/28505569
序言
求兩個正整數的最大公約數是一個很古老且很基本的問題,歐幾里得在其著作《幾何原本》中給出了高效的解法——輾轉相除法,也叫做歐幾里得算法。下面我們來看下求最大公約數的一些方法。
方法一
我們先來看歐幾里得的輾轉相除法。原理很簡單,假設用f(x,y)表示x和y的最大公約數,我們令x>y,則有x=ky+b,如果一個數能夠同時整除x和y,則必能同時整除b和y,而能夠同時整除b和y的數也必能同時整除x和y,即x和y的公約數與b和y的公約數相同,因此二者的最大公約數也相同,則有f(x,y)=f(y,x%y),一直輾轉相除,最終當其中一個爲0時,剩下的另一個就是二者的最大公約數。一個例子如下所示:
f(42,30) = f(30,12) = f(12,6) = f(6,0) = 6
輾轉相除法的代碼實現如下:
/*
歐幾里得算法,輾轉相除求最大公約數
*/
int MaxYue1(int a,int b)
{
//在輾轉相除之前,確保a比b大
if(a<b)
{
int temp = a;
a = b;
b = temp;
}
//輾轉相除法球最大公約數
while(b!=0)
{
int temp = a%b;
a = b;
b = temp;
}
return a;
}
該方法用到了取模運算,編譯器在執行取模運算時,會轉化爲相應的除法運算,如:x%y = x - (x%y)*y,除法運算的開銷很大,尤其對於大的整數。方法二
爲了避免除法運算帶來的大的開銷,我們可以用輾轉相減法來實現,同樣利用的原理如下:如果一個數能夠同時整除x和y,則它必能同時整除x-y和y,因此最大公約數f(x,y) = f(y,x-y),注意要保證左邊的數大於右邊的數,如果小於,則將二者進行交換。
輾轉相減法的實現代碼如下:
/*
輾轉相減法求最大公約數
*/
int MaxYue2(int a,int b)
{
if(a<b)
return MaxYue2(b,a);
if(b==0)
return a;
else
return MaxYue2(b,a-b);
}
該方法雖然避免了除法操縱帶來的大的時間開銷,但是對於大的整數,也要進行很多次的相減操作,尤其遇到兩個數相差很大的情況,比如f(1000000,1)這樣的情況,迭代相減的次數就會很多。方法三
爲了減少迭代的次數,我們考慮對上述算法進行改進,很明顯,我們可以有如下結論:
1、當x、y都爲偶數時,f(x,y) = 2*f(x/2,y/2)
2、當x爲偶數,y爲奇數時,f(x,y) = f(x/2,y)
3、當x爲奇數,y爲偶數時,f(x,y) = f(x,y/2)
4、當x,y多爲奇數時,f(x,y) = f(y,x-y)
每一次的操作必然是以上四種情況的其中一種,且我們對乘2和除2的操作可以通過移位來完成,效率很高,很明顯這種算法最壞情況下的時間複雜度爲O(log2max(x,y))(以2爲底,max(x,y)的對數),很適合對大的整數進行計算。
這種方法實現的代碼如下;
/*
改進輾轉相減法
*/
int MaxYue3(int a,int b)
{
if(a<b)
return MaxYue3(b,a);
if(b==0)
return a;
else
{
if((a&1)==0) //a爲偶數
{
if((b&1)==0) //b也爲偶數
return (MaxYue3(a>>1,b>>1)<<1);
else //b爲奇數
return MaxYue3(a>>1,b);
}
else //a爲奇數
{
if((b&1)==0) //b爲偶數
return MaxYue3(a,b>>1);
else //b也爲奇數
return MaxYue3(b,a-b);
}
}
}