1. 程式人生 > >歐幾里得演算法(求最大公因子)及擴充套件歐幾里得(求乘法逆元)

歐幾里得演算法(求最大公因子)及擴充套件歐幾里得(求乘法逆元)

一、歐幾里得演算法

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

演算法描述:

1. 輸入:兩個非負整數a,b,且a≥b

2. 輸出:a,b的最大公因子。

       (1)b≠0時,作

    r←a mod ba←bb←r

       (2) 返回(a)

程式碼遞迴實現:

int gcd(int a,int b)
{
    //如果a小於b
    if(a<b)
    {
        int temp=a;
        a=b;
        b=temp;
    }
    if(b==0)return a;
    else return gcd(b,a%b);//遞迴
}

二、擴充套件歐幾里得演算法

擴充套件歐幾里德演算法是用來在已知a, b求解一組xy,使它們滿足貝祖等式: ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。

    遞推演算法:

輸入:兩個非負整數a,ba≥b

輸出:d=gcd(a,b)與滿足ax+by=d的整數xy

1. b=0,則d←ax=1y=0返回(d,x,y)

2. x2←1x1=0y2=1y1=1

3. b>0時,作

       (1) q←a/br←a-qbx←x2-qx1y←y2-qy1。

       (2) a←bb←rx2←x1x1←xy2←y1y1←y

4. d←a

x←x2y=y2,返回(d,x,y)

遞迴演算法推導:

a>b

1,顯然當 b=0gcdab=a。此時 x=1y=0

2a>b>0

ax1+ by1= gcd(a,b);

bx2+ (a mod b)y2= gcd(b,a mod b);

據樸素的歐幾里德原理有 gcd(a,b) = gcd(b,a mod b);

:ax1+ by1= bx2+ (a mod b)y2;

:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;

說明: a-[a/b]*b即為mod運算。[a/b]代表取小於a/b的最大整數。

也就是

ax1+ by1 == ay2+ b(x2- [a / b] *y2);

根據恆等定理得:x1=y2; y1=x2- [a / b] *y2;

這樣我們就得到了求解 x1,y1 的方法:x1y1 的值基於 x2y2.

上面的思想是以遞迴定義的,因為 gcd 不斷的遞迴求解一定會有個時候 b=0,所以遞迴可以結束。

乘法逆元:

gcd(r,m)=1,則存在整數s,使得rs1(mod m) 。整數s也稱為r模整數m下的乘法逆元。那麼當gcd(a,b)=1,有ax+by = gcd(a, b)=1,那麼通過計算得出(b+x%b)%b的值就是ab模下的乘法逆元。

遞推程式碼實現:

int gcd(int a,int b)
{
    //如果a小於b
    if(a<b)
    {
        int temp=a;
        a=b;
        b=temp;
    }
    if(b==0)return a;
    else return gcd(b,a%b);//遞迴
}


int ex_Gcd(int a,int b,int &x,int &y)
{
    int x1=0,x2=1,y1=1,y2=0,q,d,r;
    //最大公因子
    d=gcd(a,b);
    if(b==0)
    {
        d=a;
        x=1;
        y=0;
        return d;
    }
    while(b>0)
    {
        q=a/b;
        r=a-q*b;
        x=x2-q*x1;
        y=y2-q*y1;
        a=b;
        b=r;
        x2=x1;
        x1=x;
        y2=y1;
        y1=y;
    }
    d=a;
    x=x2;
    y=y2;
    return d;

}

int main()
{
    int x,y;
    int a,b;
    cin>>a>>b;
    ex_Gcd(a,b,x,y);
    if(gcd(a,b)==1)
    {
        cout<<"存在a的乘法逆元:"<<(b+x%b)%b<<endl;
    }else{
        cout<<"不存在a的乘法逆元!"<<endl;
    }
    cout<<"x:"<<x<<endl;
    cout<<"y:"<<y<<endl;
    return 0;
}

遞迴程式碼實現:

int gcd(int a,int b)
{
    //如果a小於b
    if(a<b)
    {
        int temp=a;
        a=b;
        b=temp;
    }
    if(b==0)return a;
    else return gcd(b,a%b);//遞迴
}


int ex_Gcd(int a, int b, int& x, int& y)
{
    int d;

    if(b!=0){
        d = ex_Gcd(b, a % b, y, x);
      //cout<<"a:"<<a<<endl;
      //cout<<"b:"<<b<<endl;
      //cout<<"x:"<<x<<endl;
      // cout<<"y:"<<y<<endl<<endl<<endl;
        y = y-(a / b) * x;

    }else {
        x = 1;
        y = 0;
    }
    return d;
}

int main()
{
    int x,y;
    int a,b;
    cin>>a>>b;
    ex_Gcd(a,b,x,y);
    if(gcd(a,b)==1)
    {
        cout<<"存在a的乘法逆元:"<<(b+x%b)%b<<endl;
    }else{
        cout<<"不存在a的乘法逆元!"<<endl;
    }
    cout<<"x:"<<x<<endl;
    cout<<"y:"<<y<<endl;
    return 0;
}