計算兩個數的最大公約數 gcd(a,b) && 證明歐幾里得算法


求兩個數a和b的最大公約數,可以想到的是從[1,min(a,b)]枚舉每個正整數:

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    int ans=1;
    for(int i=2;i<=min(a,b);++i)
    {
        if(a%i==0 && b%i==0)
            ans=i;
    }
    return ans;
}
int main()
{
    int a,b;
    cin>>a>>b;
    cout<<gcd(a,b);
    return 0;
}

 

不過當a和b規模比較大時,這種算法是不夠快的。有更快更優雅的算法。

 

 

  • 首先給出一個定理:

gcd(a,b)=gcd(b,a-b)  (a>=b)

 

證明:

設gcd(a,b)=m (m>=1)

則a%m=0,b%m=0

(a%m-b%m)%m=0

(a-b)%m=0

因為a%m=0,b%m=0,(a-b)%m=0,gcd(a,b)=m

所以gcd(a,b,a-b)=m;

下面證明gcd(b,a-b)=m,然后可以得到gcd(a,b)=gcd(b,a-b)。

設c=a-b

因為gcd(a,b,c)=m;

所以gcd(b,c)!=m的充要條件是存在一個數d (d>=1)使得(b/m)%d=0且(c/m)%d=0且(a/m)%d!=0。

下面用反證法:

設存在這樣的d

(c/m)%d=0

((a-b)/m)%d=0

((a/m)-(b/m))%d=0

((a/m)%d-(b/m)%d)%d=0

已知(b/m)%d=0,代入得((a/m)%d)%d=0

又已知(a/m)%d!=0,所以(a/m)%d的結果屬於(0,d)

而x屬於(0,d),x%d不可能等於0,因此矛盾。

所以不存在這樣的d

所以gcd(b,a-b)=gcd(b,c)=m

gcd(a,b)=gcd(b,a-b) (a>=b) 該定理證明完畢


於是就可以用這個算法來計算,其中gcd(a,0)=a:

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    if(b==0)
        return a;
    if(a<b)
        swap(a,b);
    return gcd(b,a-b);
}
int main()
{
    int a,b;
    cin>>a>>b;
    if(a<b)
        swap(a,b);
    cout<<gcd(a,b);        
    return 0;
}

當然數據規模大的時候棧可能會溢出,所以改成非遞歸即可。


還可以更快?(感謝一位同學的證明)


 

  • 再給出第二個定理:

gcd(a,b)=gcd(b,a-k*b) 其中k=0,1,2,3,4...且a>=k*b

 

這個定理證明同上


 

  • 經化簡可得下面這個定理:

gcd(a,b)=gcd(b,a%b)

 

這就是輾轉相除法(歐幾里得算法)。

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
int main()
{
    int a,b;
    cin>>a>>b;
    cout<<gcd(a,b);        
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM