扩展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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章