扩展欧几里得算法

欧几里得算法

  • 求最大公约数
int gcd(int a, int b) {
    while (b) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
  • 求最小公倍数
int lcm(int a, int b) {
    return a * b / gcd(a, b);
}

扩展欧几里得算法

  • 贝祖定理

    • 即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。

    • 得到x,y后,可以将x+b,y-a,结果不变

    • 换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)

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

  • 证明

    • 当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a1+b0=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-(a/b)*b;带入:

b*x1 + (a-(a/b)*b)*y1

= b*x1 + a*y1 – (a/b)*b*y1

= a*y1 + b*(x1 – a/b*y1) = gcd 
 
 发现 x = y1 , y = x1 – a/b*y1

  • 这样我们就得到了每两个相邻状态的x和y的转化,就可以在求gcd的同时对x和y进行求值了

  • 模板

int exgcd(int a, int b, int &x, int &y) {//扩展欧几里得算法
    if (b == 0) {
        x = 1;
        y = 0;
        return a;  //到达递归边界开始向上一层返回
    }
    int r = exgcd(b, a % b, x, y);
    int temp = y;    //把x y变成上一层的
    y = x - (a / b) * y;
    x = temp;
    return r;     //得到a b的最大公因数
}

题目

洛谷1082同余方程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sq4xkxPa-1583633343499)(http://106.15.192.117:233/upload/2020/1/image-e538bcda409d45d2a66d31a5c8212ad6.png)]

  • 直接枚举肯定会超时,我们所以我们要考虑ext-gcd

  • 我们考虑ax+by=1,对此式左右两方同时mod b,那么一定有 ax=1(mod b)
    但是求得了x还不一定满足题目要求,可能是负数,还可能太大,我们再将其多次加上或者减去b直到满足要求即可。 x=((x%b)+b)%b;

int x, y, c, d;

int main() {
    scc(c, d);
    exgcd(c, d, x, y);
    x = ((x % d) + d) % d;
    p(x);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值