擴展Euclidean算法及其應用(乘法逆元,模線性方程)

提到Euclidean算法就必須先講一講經典的求最大公約數算法

歐幾里德算法又稱輾轉相除法,用於計算兩個正整數a,b的最大公約數。——來自百度百科。

Euclidean算法求兩個正整數abgcd。先令r0ar1b,接着執行如下運算:

使用gcd(a,b)表示a與b的最大公約數,則使用歐幾里德算法求取最大公約數的實現代碼如下:

//Euclidean算法求gcd
int gcd( int a, int b ) {
    return b == 0 ? a : gcd( b, a%b );
}

同時最小公倍數可通過以下代碼實現:

lcm( a, b ) = a / gcd( a, b ) * b;


以下介紹擴展歐幾里德算法:
定理:對於不完全爲0的非負整數a,b,gcd(a,b)表示a,b的最大公約數d,必然存在整數對x,y
使得gcd(a,b)=d=ax+by。
對於gcd(a,b) = d,對(a, b)用歐幾里德輾轉相除會最終得到(d, 0)。此時對於把a =d, b = 0代入a*x + b*y = d,顯然x = 1,y可以爲任意值。
我們可以用a = d, b = 0的情況逆推出來任何gcd(a, b) = d 滿足a*x + b*y = d的解。如果x0,y0是b*x + (a%b)*y = d 的解,那麼對於a*x + b*y = d的解呢?
b*x + (a%b)*y = d →
b*x + (a - [a/b]*b)*y = d →
a*y + b*(x - [a/b]*y)= d
所以a*x + b*y =d的解
x1 = y0,y1= x0 -[a/b]*y0;
由以上過程可以得出擴展歐幾里德算法執行過程:

//擴展歐幾里德算法
int exgcd( int a, int b, int& x, int& y ) {
    if( b == 0 ) {
        x = 1;
        y = 0;
        return a;
    }
    int ret = exgcd( b, a % b, x, y );
    int tmp = x;
    x = y;
    y = tmp - a / b * y;
    //y = ( ( tmp - a / b * y ) % mod + mod ) % mod;
    return ret;
}//獲取gcd(a,b)=ax+by中的x,y的值

接下來介紹乘法逆元:

若m≥1,(a,m)=1,則存在c使得 ca≡1(mod m) 我們把c稱爲是a對模m的逆,記爲 a-1(mod m)或a-1 可以用擴展歐幾里德算法求a-1
應用: 求(a/b)%c時,若a爲大整數時可以寫成 (a%c)*b-1

還可以使用擴展歐幾里德算法求乘法逆元:

/*******************************************
擴展歐幾里得算法求乘法逆元
調用:exgcd( a, b, x, y );
則:  x爲a模b的乘法逆元,y爲b模a的乘法逆元
********************************************/
下面通過一個例子來詳細介紹該怎麼用擴展歐幾里得算法求乘法逆元:

hdoj1576
題目要求給定兩個數A和B,去求(A/B)%9973(由於A太大,故只給出n(n=A%9973) ),就是一個求B的乘法逆元的裸題,直接上代碼:

/***********************
OJ: HDOJ
ID: forever
TASK: 1576.A/B
LANG: C++
NOTE: exgcd求乘法逆元
***********************/
#include <cstdio>
#define mod 9973

int exgcd( int a, int b, int& x, int& y ) {
    if( b == 0 ) {
        x = 1;
        y = 0;
        return a;
    }
    int ret = exgcd( b, a % b, x, y );
    int tmp = x % mod;
    x = y % mod;
    y = ( ( tmp - a / b * y ) % mod + mod ) % mod;
    return ret;
}

int main()
{
    int t, n, b;
    scanf( "%d", &t );
    while( t -- ) {
        scanf( "%d%d", &n, &b );
        int x, y;
        x = y = 0;
        int ret = exgcd( b, mod, x, y );
        printf( "%d\n", ( x * n ) % mod );
    }
}

最後再介紹模線性方程的求解:

定理:方程ax=b(mod n)對於未知量x有解,當且僅當gcd(a, n)|b

定理:方程ax=b(mod n)或者對模n有d個不同的解,其中d=gcd(a, n)或者無解。

定理:設d=gcd(a, n),假定對整數x’和y’,有d=ax’+ny’。如果d|b,則方程ax=b(modn)有一個解的值爲x0,滿足x0=x’(b/d)mod n。

定理:假設方程ax=b(mod n)有解(即有d|b,其中d=gcd(a, n)),x0是該方程的任意一個解,則該方程對模n恰有d個不同的解,分別爲:xi=x0+i(n/d)(i = 1, 2, …, d-1)。

例:POJ2115

/*
OJ: POJ
ID: 3013216109
TASK: 2115.C Looooops
LANG: C++
NOTE: exgcd求解模線性方程
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll __int64
using namespace std;

ll mod;

ll exgcd( ll a,ll b,ll& x,ll & y ) {
    if( b == 0 ) {
        x = 1;
        y = 0;
        return a;
    }
    ll ret = exgcd( b, a % b, x, y );
    ll tmp = x % mod;
    x = y % mod;
    y = ( ( tmp - a / b * y ) % mod + mod ) % mod;
    return ret;
}

int main()
{
    ll a, b, c, x, y;
    ll k;

    while( scanf( "%I64d%I64d%I64d%I64d", &a,&b,&c,&k) ) {
        if( a == 0 && b == 0 && c == 0 && k == 0)
            break;
        mod = (ll)1 << k;
        ll temp = ( ( b - a) % mod + mod ) % mod;
        x = y = 0;
        ll ret = exgcd( c, mod, x, y );
        if( temp % ret == 0) {
            x = ( x * ( temp / ret ) ) % mod;
            x = ( x % ( mod / ret ) + mod / ret ) % ( mod / ret );
            printf( "%I64d\n", x );
        }
        else
            puts( "FOREVER" );
    }
    return 0;
}





發佈了29 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章