【数据结构与算法】扩展欧几里得算法详解


这个算法还是有点意思的,需要一些思考和理解。

如何理解?

为了介绍扩展欧几里得,我们先介绍一下裴蜀定理

        即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)

有一个直接的应用就是 如果ax+by=1有整数解,那么gcd(a,b)=1;

要求出这个最大公因数gcd(a,b),我们最容易想到的就是古老悠久而又相当强大的辗转相除法:

int gcd(int a, int b) {
    return  b ? gcd(b, a % b) : a;
}

但是,对于上面的式子ax+by=m来说,我们并不仅仅想要知道有没有解,而是想要知道在有解的情况下这个解到底是多少。

所以,扩展欧几里得

        当到达递归边界的时候,b=0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a*1+b*0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。

初步想法:由于是递归的算法,如果我们知道了这一层和上一层的关系,一层一层推回去,就可以推到最开始的。类似数学上的数学归纳法

假设当前我们在求的是a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得 b*x1+(a%b)*y1=gcd(a, b)

(注意在递归算法中,永远都是先得到下面一个状态的值)

这时我们可以试着去寻找这两个相邻状态的关系:
首先我们知道:a%b=a-(a/b)*b;
代入:

b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1         //合并a,b同类项
= a*y1 + b*(x1 – a/b*y1) = gcd(a, b)          //发现 x = y1 , y = x1 – a/b*y1(关系(1))

这样来理解(不同层的x,y值存在上述关系(1),满足这一条件的前提下):

14*x + 6*y = gcd         //第1层
6*x1 + (14%6)*y1 = gcd         //往下一层推,第2层
2*x2 + (6%2)*y2 = gcd         //往下一层推,第3层,到达递归边界

通过这样的递推,即从上往下一层推(或者用递归的思想,一直归到最底,然后再计算回来)就能实现这一算法了。

总结:每一组a,b值更新后,其最大公约数gcd不变,x,y满足一定关系同步更新,组合后即ax+by=gcd(a,b)等式恒成立。最后,推荐用数学归纳法对递归代码进行验证,加深理解。


实现代码

#include <stdio.h>

int ex_gcd(int a, int b, int *x, int *y) { //指针传入
    if (!b) {
        *x = 1, *y = 0;
        return a; //到达递归边界,开始回代
    }
    int xx, yy, ret = ex_gcd(b, a % b, &xx, &yy);
    *x = yy; //返回时,计算上一层x,y值
    *y = xx - a / b * yy;
    return ret; //gcd值
}

int exgcd(int a, int b, int &x, int &y) { //引用传入
    if (!b) {
        x = 1; y = 0;
        return a; 
    }
    int ret = exgcd(b, a % b, x, y);
    int temp = y; 
    y = x - (a / b) * y;
    x = temp;
    return ret; 
}

int main() { //测试代码
    int a, b, x, y;
    while (~scanf("%d%d", &a, &b)) {
        printf("ex_gcd(%d, %d) = %d\n", a, b, ex_gcd(a, b, &x, &y));
        printf("exgcd(%d, %d) = %d\n", a, b, exgcd(a, b, x, y));
        printf("%d * %d + %d * %d = %d\n", a, x, b, y, a * x + b * y);
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idiot5liev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值