1. 程式人生 > >歐幾里得+擴充套件歐幾里得

歐幾里得+擴充套件歐幾里得

歐幾里得演算法

歐幾里德演算法又稱輾轉相除法,用於計算兩個正整數a,b的最大公約數(gcd)。

其計算原理依賴於下面的定理:

定理:gcd(a,b) = gcd(b,a mod b) (a>b 且a mod b 不為0)

證明:a可以表示成a = kb + r,則r = a mod b

假設d是a,b的一個公約數,則有

d|a,d|b,而r = a - kb,因此d|r

因此d也是(b,a mod b)的公約數

因此(a,b)和(b,a mod b)的公約數是一樣的,其最大公約數也必然相等,得證

或:證明:

第一步:令c=gcd(a,b),則設a=mc,b=nc

第二步:根據前提可知r =a-kb=mc-knc=(m-kn)c

第三步:根據第二步結果可知c也是r的因數

第四步:可以斷定m-kn與n互素【否則,可設m-kn=xd,n=yd,(d>1),則m=kn+xd=kyd+xd=(ky+x)d,則a=mc=(ky+x)dc,b=nc=ycd,故a與b最大公約數≥cd,而非c,與前面結論矛盾】

從而可知gcd(b,r)=c,繼而gcd(a,b)=gcd(b,r),得證。

歐幾里得(GCD)de遞迴寫法:

int gcd(int a,int b)
{
    if(b == 0) 
        return a;
    else
        return gcd(b,a%b);
}
//更簡單寫法
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;//(或return b==0?a:gcd(b,a%b);)
}

由最大公約數又可得最小公倍數(LCM)

lcm(a,b) = (a*b)/gcd(a,b); 程式碼如下:

int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}
//為了防止a*b超long long 溢位,所以建議下面這種寫法

long long(long long a,long long b)
{
    return a/gcd(a,b)*b;
}

gcd  lcm  兩個公式的推廣:

gcd(k*a,k*b) = k*gcd(a,b);

lcm(k*a,k*b) = k*lcm(a,b);

//這兩個公式利用

gcd(a/c,b/c) = 1   (注:c = gcd(a,b))可以推理證明

擴充套件歐幾里得演算法

內容:若gcd(a,b) = d,那麼一定存在x,y使得 ax+by = d,這是一個不定方程,一定有多組解,但是隻要找到一組特解x0,y0,就能得到不定方程的通解:

x=x0+b/gcd*k; 

y=y0+a/gcd*k;(k為整數)

擴充套件歐幾里得演算法就是在求a,b的最大公約數的同時順帶著把ax+by=d的通解求出來的過程,程式碼與歐幾里得差不多,也是採用的遞迴寫法 :  tx=y;   ty=x-(a/b)*y(兩個相鄰狀態之間的關係,證明過程不會= =);

//擴充套件歐幾里得
ll exgcd(ll a,ll b,ll &x,ll &y){
    //注意x和y必須是引用
    if(!b){x=1,y=0;return a;}
    int d=exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-(a/b)*y;
    return d;
}

擴充套件歐幾里得可以判斷不定方程ax+by=c是否有整數解,好像歐幾里得也是可以判斷的-_-||,d=gcd(a,b);如果c可以整除d,那麼不定方程ax+by=c有整數解,否則沒有。

ax+by=c;
d=gcd(a,b);
if(d%c==0)//有整數解
{
    x=c/d*x0+b/d*k;
    y=c/d*y0-a/d*k;//k為整數
}
else
{
    無整數解
}
因為 ax0+by0=gcd 
所以 a(x0+b/gcd*t)+b(y0-a/gcd*t)=gcd 
所以 x=x0+b/gcd*t, y=y0-a/gcd*t (t為迴圈變數)

擴充套件歐幾里得還能求逆元(*^▽^*)

使用條件: a,b為正整數,而且gcd(a,b) = 1

證明: 因為a,b 互質,所以一定有 ax+by = 1

兩邊同時對b 取餘

ax%b + by %b = 1%b   ------->   ax%b = 1%b

即 ax ≡ 1 (mod b)

擴充套件歐幾里得中x 就是a關於b的逆元

同理y 就是 b 關於 a的逆元

所以使用完歐幾里得演算法,我們判斷返回值d 是否為1,為1說明 gcd(a,b) = 1

即求得的x就是 a關於b的逆元

void inv(ll a,ll b)
{
    ll x,y;
    if(exgcd(a,b,x,y) == 1)
        cout<<"inv(a):"<<x<<endl;
    else
        cout<<"不存在逆元"<<endl;
}

UPC 9511 Utawarerumono歐幾里得判斷不定方程是否有整數解,本來以為需要擴充套件歐幾里得,後來發現歐幾里得加暴力就可以,不過要判斷a,b,c取值的多種情況

AC程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//ll exgcd(ll a,ll b,ll &x,ll &y){
//    //注意x和y必須是引用
//    if(!b){x=1,y=0;return a;}
//    int d=exgcd(b,a%b,x,y);
//    int t=x;x=y,y=t-(a/b)*y;
//    return d;//d是a和b的gcd,順便求出來
//}
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    ll t,ans;
    ll a,b,c,p1,p2,q1,q2;
    scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&p1,&p2,&q1,&q2);
//    ll x,y;
    ll d=gcd(a,b);
    if(a==0&&b==0&&c==0)
    {
        printf("0\n");
        return 0;
    }
    if(a==0&&b==0&&c!=0)
    {
        printf("Kuon\n");
        return 0;
    }
    if(a!=0&&b==0)
    {
        if(c%a!=0)
        {
            printf("Kuon\n");
        }
        else
        {
            t=c/a;
            ans=p2*t*t+p1*t;
            printf("%lld\n",ans);
        }
        return 0;
    }
    if(a==0&&b!=0)
    {
        if(c%b!=0)
        {
            printf("Kuon\n");
        }
        else
        {
            t=c/b;
            ans=q2*t*t+q1*t;
            printf("%lld\n",ans);
        }
        return 0;
    }
    if(c%d==0)
    {
        ll minn=1e18;
        for(ll i=-1e6;i<=1e6;i++)
        {
            if((c-a*i)%b==0)
            {
                ll t=(c-a*i)/b;
                ll ans=p2*i*i+p1*i+q2*t*t+q1*t;
                minn=min(minn,ans);
            }
        }
        printf("%lld\n",minn);
    }
    else
        printf("Kuon\n");
    return 0;
}

HDU 2669 擴充套件歐幾里得裸題

這道題只需要注意兩點:gcd(a,b)=1(這個好辦,求gcd就行了);還有就是x>=0,這就需要用到擴充套件歐幾里得的通解公式了,如果x<0,可以把x一直加b/gcd(a,b),y一直減a/gcd(a,b),直到x>=0為止。

AC程式碼:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return d;
}
int main()
{
    ll a,b,x,y;
    while(cin>>a>>b)
    {
        ll d=exgcd(a,b,x,y);
        if(d==1)
        {
            while(x<0)
            {
                x+=b;
                y-=a;
            }
            cout<<x<<" "<<y<<endl;
        }
        else
            cout<<"sorry"<<endl;
    }
    return 0;
}