求一個數的最大公約數的三種思路——解題筆記

bigface1234fdfg發表於2015-02-03

求一個數的最大公約數的三種思路——解題筆記

    

    程式設計之美上的題目:求一個數的最大公約數。

    這道題目有三種解題思路,總結如下:


思路一:


    直接使用輾轉相除法,這個不多介紹,程式碼如下:


// 直接輾轉相除法
int gcd1(int a, int b)
{
	for(int m = a%b; m != 0; m = a%b)
	{
		a = b; 
		b = m; 
	}
	return b; 
}

分析:輾轉相除法需要用到數值之間的取餘運算,這是非常耗時間的。


思路二:


    改進輾轉相除法中取餘(除法)運算,改為減法。這裡可以利用一個規律,x和y的最大公約數等於x-y和y的最大公約數。不過需要判斷x和y的大小。

   程式碼如下:

// x和y的最大公約數等於x-y和y的最大公約數
int gcd2(int a, int b)
{
	if(a < b)
		swap(a, b);  // 預設前面的數較大
	while(b)
		return gcd2(b, a-b);  // 遞迴
	return a; 
}


分析:雖然這種方法只有減運算,用的是遞迴的實現形式。但是,需要遞迴的次數比較多,假如a和b之間相差較大,那麼每次都只是相減,會迭代很多次。


思路三:


    可以結合前面兩個思路的優點,我們不全都是用減運算,偶爾用一次除法,不過只是除以2,即判斷奇偶數,而且用移位運算實現。



程式碼如下:

// 通過判斷奇偶數,減少迭代次數
bool isEven(int a) 
{
	return !(a & 1);  // 按位與來判斷奇偶數
}

int gcd3(int a, int b)
{
	if(a < b)
		swap(a, b);  // 預設前面的數較大

	if(!b)
		return a; 

	if(isEven(a))  // if a is even
	{
		if(isEven(b))  // b is even at the same time
			return (gcd3(a>>1, b>>1) <<1); // note that *2
		else
			return (gcd3(a>>1, b));  // if only a is even
	}
	else
	{
		if(isEven(b))
			return gcd3(a, b>>1); // is only b is even 
		else 
			return gcd3(a-b, b);  // neither a or nor b is even 
	}
}

分析:時間複雜度只是a和b中較大數的二進位制位數,也就是O(log max(a, b))。注意其中判斷是否為奇偶數用的是位運算。






相關文章