---恢復內容開始---
最近在看一本算法的書。講的都是一些基本的問題,並沒有涉及很復雜的算法,或者說這本書更看重技巧。
其中開篇就講了最大公約數的算法,覺得有可取之處,和大家分享一下。
提到最大公約數我們最先想到的一定是輾轉相除法。
沒錯,永遠不要蔑視我們的祖先,他們的智慧是無窮的。(扯遠了,嘿嘿)
我們都在用輾轉相除法來求最大公約數,卻很少去想為什么輾轉相除法就能求最大公約數呢?或者說怎么證明算法的正確性呢(至少我之前完全沒有想過)。
這里我們感性的認識一下輾轉相除法(不是很嚴格地證明一下)。
假設兩個數a,b且a>b。 設a除以b商k,余數為r,那么會有a=k*b+r,那么b和r的最大公約數,就是a和b的最大公約數。所以問題就轉換求成除數和余數的最大公約數,依次遞歸,遞歸的出口就是一個已知的條件:如果a能夠被b整除,那么b就是a和b的最大公約數,所以輾轉相除法遞歸代碼如下:
int GCD1(int num1,int num2)
{
if(num1%num2==0)
{
return num2;
}
else
{
int next1=num2;
int next2=num1%num2;
return GCD1(next1,next2);
}
}
還有一種我們耳熟能詳的求最大公約數的算法就是更相減損術,他的基本思想就是:兩個數a,b且a>b,那么令c=a-b,然后把 b和c看成新的a和b,遞歸下去,遞歸出口就是一個已知的條件:如果a=b,那么a和b的最大公約數就是a或b。其實更相減損術和輾轉相除法是一個東東,更相減損術就是讓輾轉相除法中的商(k)恆為1,所以大多數情況下,輾轉相除法的效率要比更相減損術的效率高。給出更相減損術的代碼:
int GCD2(int num1,int num2)
{
if(num1==num2)
{
return num2;
}
else
{
int next1= (num1>num2)? (num1-num2):(num2-num1);
int next2= (num1>num2)?num2:num1;
if(next1>next2)
{
return GCD2(next1,next2);
}
else
{
return GCD2(next2,next1);
}
}
}
好了,步入這次的正題:多個數求最大公約數(實際上就是輾轉相除法的擴展)給出算法:
設一組數a1,a2,a3,a4,a5..
(1)對這一組數進行排序(從大到小)
(2)對每兩個相鄰的兩個數進行如下操作:
設相鄰的兩個數為A和B(A在前,因為已經排序,所以A>B),如果A=n*B(n為整數),也就是A能夠被B整除,那么就令A=B;如果A不能被B整除則令A=A%B。
(3)重復(1)、(2)知道數組中每個數都相等,則最大公約數就為這個數。
給出完整程序:
#include<iostream>
using namespace std;
void Sort(int* num,int n);
int GCD3(const int* num,int n);
int main()
{
int num[4]={756,504,630,2226};
int result=GCD3(num,4);
cout<<"數組:";
for(int i=0;i<4;i++)
{
cout<<num[i]<<" ";
}
cout<<"的最大公約數為:"<<result<<endl;
return 0;
}
int GCD3(const int* num,int n)
{
int *temp=new int[n];
for(int i=0;i<n;i++)
{
temp[i]=num[i];
}
do
{
if(temp[0]==temp[n-1])
{
break;
}
else
{
Sort(temp,n);//排序
for(int i=0;i<n-1;i++)
{
if(temp[i]%temp[i+1]==0)
{
temp[i]=temp[i+1];
}
else
{
temp[i]=temp[i]%temp[i+1];
}
}
}
}while(temp[0]!=temp[n-1]);
return temp[0];
}
//排序
void Sort(int* num,int n)
{
//冒泡排序法
for(int i=0;i<n-1;i++)
{
for(int j=i;j<n-1;j++)
{
if(num[i]<num[j+1])
{
int temp=num[i];
num[i]=num[j+1];
num[j+1]=temp;
}
}
}
}
---恢復內容結束---
