扩展欧几里得算法详解



对于不完全为 0的非负整数abgcd(a,b)表示ab的最大公约数,必然存在整数xy,使得gcd(a,b)=ax+by


解析:

ab不全为0,令a>b

b=0时,gcd(a,b)=a,解的情况为x=1y=0

ab=0,令a*x1+b*y1 = gcd(a,b),所以b*x2+(a%b)*y2 =gcd(b,a%b)

gcd(a,b) = gcd(b,a%b)(欧几里得算法)

故有a*x1+b*y1 = b*x2+(a%b)*y2

 = b*x2+( a-(a/b)*b )*y2
              = b*x2+a*y2-(a/b)*b*y2
              = a*y2+b*(x2-(a/b)*y2)
即有x1=y2, y1=x2-(a/b)*y2
因此x1y1的值可由x2y2推知,拓展欧几里得算法的求解过程就是不断地的将b放小,直至b等于0,最后反推求xy

//Date:2015.05.05
//扩展欧几里得算法的递归与非递归实现

#include <ctime>
#include <random>
#include <cstdio>
using namespace std;

int recursive_gcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    int r=recursive_gcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}

int iterative_gcd(int m,int n,int &x,int &y){
    int x1,y1,x0,y0;
    x0=1;x1=0;x=0;
    y0=0;y1=1;y=1;

    int r=m%n;
    int q=m/n;
    while(r){
        x=x0-q*x1;x0=x1;x1=x;
        y=y0-q*y1;y0=y1;y1=y;
        m=n; n=r; r=m%n;
        q=m/n;
    }
    return n;
}

int main(){
    int a,b,x,y,r;
    mt19937 engine(time(NULL));
    uniform_int_distribution<int> distribution(0,100000);
    a=distribution(engine);b=distribution(engine);

    printf("a*x+b*y=gcd(a,b)\n");
    r = recursive_gcd(a,b,x,y);
    printf("递归 : (%d) * (%d) + (%d) * (%d) = %d\n",a,x,b,y,r);
    r = iterative_gcd(a,b,x,y);
    printf("迭代 : (%d) * (%d) + (%d) * (%d) = %d\n",a,x,b,y,r);
    return 0;
}

应用:

1.求解不定方程ax+by=c :

对于不定整数方程ax+by=c ,若cmod gcd(a,b)=0,则该方程存在整数解,否则不存在整数解。 

故当c mod gcd(a,b)=0时,先用扩展欧几里得算法求出ax+by=gcd(a,b)的一组解x1,y1

k= c/gcd(a,b);则x2=x1*ky2=y1*kax+by=c的一组解。

x=x2+b/gcd(a,b)*ty=y2-a/gcd(a,b)*t (t为整数),即为ax+by=c的所有解。

 

2.求解乘法逆元

在密码学中,有限域GF(p)是一个很重要的域,其中p为素数。简单来说,GF(p)就是mod p,因为一个数模p后,结果在[0, p-1]之间。GF(p)里面的乘法即为一般的乘法运算。为什么p一定要是一个素数呢?这是因为当p为素数时,才能保证集合中的所有的元素都有乘法逆元(0除外)。假设p等于10,对于元素2,找不到一个数x,使得2*x mod10等于1。如果p是素数,那么对于域中的任一个元素a,总能在域中找到另外一个元素x,使得a*xmod p等于1,其中a与互为乘法逆元。也即若gcd(a,p)=1;则ax+py=1

 

但是我们希望0255256个数字也能组成一个域,因为很多领域需要用到。mod 256的余数范围是0255,不过256不是素数。为此我们定义一个素多项式m(x)=x^8+ x^4 + x^3 +x +1(类似素数,素多项式不能表示为其他两个多项式的乘积),于是0255可以通过mod m(x)这样的方式组成一个域GF(2^8),域中的每一个元素都存在乘法逆元(0的乘法逆元定义为0)

注:有限域GF(2^8)中的加法运算为异或运算,减法与加法等价。

//Date:2015.05.05
//扩展欧几里得算法求解有限域GF(2^8)内的乘法逆元

#include<cstdio>
using namespace std;
const int Bit_Num=sizeof(int)*8;

//求解非零最高位
int index_of_max(int value){
    int index=0;
    for(int i=0;i<Bit_Num;++i)
        if(value & (1<<i))
            index=i;
    return index;
}

//有限域GF(2^8)内的除法运算
int divide(int m,int b,int &r){
    int m_MSB=index_of_max(m);
    int b_MSB=index_of_max(b);
    if(m_MSB<b_MSB){
        r=m;
        return 0;
    }
    int d=m_MSB-b_MSB;
    int temp=b;
    temp=temp<<d;
    m=m^temp;
    return (1<<d)|divide(m,b,r);
}

//迭代求解x,y
int iterate(int d0,int q,int d1){
    int value=0;
    for(int i=0;i<Bit_Num;++i){
          if(q & (1<<i))
             value=value^((d1<<i));
    }
    return d0^(value);
}

int exgcd(int m,int a,int &x,int &y){
    int x0,x1,y0,y1,q,r=0;
    x0=0;x1=1;
    y0=1;y1=0;

    while(1){
        if(a==0)
            return m;
        if(a==1)
            return a;
        q=divide(m,a,r);
        x=iterate(x0,q,x1);x0=x1;x1=x;
        y=iterate(y0,q,y1);y0=y1;y1=y;
        m=a;a=r;
    }
}

int main(void){
    int m=283,a,x,y;    //m(x) = x^8 + x^4 + x^3 + x + 1;

    while(1){
        printf("请输入:");
        scanf("%X",&a);
        if(a==0)    break;
        exgcd(m,a,x,y);
        printf("%X * %X + %X * %X = 1\n",a,x,m,y);
        printf("%X的乘法逆元是%X\n\n",a,x);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值