欧几里得除法以及拓展

最大公约数

暴力

时间复杂度O(min(a,b))

辗转相除

辗转相除法,又名欧几里得除法,目的是求出两个正整数的最大公约数。

两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。比如10和25,25除以10商2余5,那么10和25的最大公约数,等同于10和5的最大公约数。

int gcd(int a, int b) {
  if (!b || a == b) return a;
  if (a > b) return gcd(b, a % b);
  else return gcd(a, b % a);
}

时间复杂度近似O(logmin(a,b))

更相减损术

辗转相除法涉及取模运算,当整数较大时,取模运算的效率比较差。这时可以看更相减损术。

更相减损术, 出自于中国古代的《九章算术》,也是一种求最大公约数的算法。两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。比如10和25,25减去10的差是15,那么10和25的最大公约数,等同于10和15的最大公约数。

int gcd(int a, int b) {
  if (a == b) return a;
  if (a > b) return gcd(b, a - b);
  else return gcd(a, b - a);
}

时间复杂度O(max(a,b)))

辗转相除结合更相减损术

更相减损术不稳定,运算次数更大,尤其是当两个数比较悬殊的时候。考虑两者结合:

a偶b偶,gcd(a,b)=2gcd(a/2,b/2)=2gcd(a>>1,b>>1)

a偶b奇,gcd(a,b)=gcd(a/2,b)=gcd(a>>1,b)

a奇b偶,gcd(a,b)=gcd(a,b/2)=gcd(a,b>>1)

a奇b奇,利用更相减损术运算一次,gcd(a,b)=gcd(b,ab), 此时a-b必然是偶数,又可以继续进行移位运算。

int gcd(int a, int b) {
  if (a == b) return a;
  if (a & 1) {
    if (b && 1) return gcd(a, std::abs(a - b));
    else return gcd(a, b >> 1);
  }
 	else {
    if (b & 1) return gcd(a >> 1, b);
    else return 2 * gcd(a >> 1, b >> 1);
  }
}

时间复杂度O(logmax(a,b)))

参考

https://houbb.github.io/2017/08/23/math-03-common-gcd-03

https://zhuanlan.zhihu.com/p/31824895

最小公倍数

a * b / gcd(a, b)

扩展欧几里得

原理

可用于求 ax+by=gcd(a, b) 的一组解(x1, y1)

(1)ax1+by1=gcd(a, b)

下面是简要求解过程。

用b与a % b来替代a和b,得到

(2)bx2+(a % b) y2=gcd(b, a % b)

根据欧几里得算法以及a % b的算数表达形式

(3)gcd(a, b) = gcd(b, a % b)(4)a % b = a  abb

联立式子(2)(3)(4)

(5)ay2 + b[x2  aby2] = gcd(a,b)

联立式子(1)(5)

(1)x1 = y2(2)y1 = x2  aby2

由此写出以下程序。

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 t = y;
    y = x - (a / b) * y;     //2的情况
    x = t;
    return r;
}

通解

如果 (x0,y0)ax+by=c 一组解,那么通解可以表示为

(3)x = x0 + bck(4)y = y0  ack

事实上

ax+by = a(x0 + bck) + b(y0  ack) = ax0+by0 = c

参考

https://zhuanlan.zhihu.com/p/100567253

https://zhuanlan.zhihu.com/p/42707457

https://www.desgard.com/algo/docs/part2/ch02/3-ext-euclidean/

https://ksmeow.moe/euclid/

https://www.cnblogs.com/wkfvawl/p/9350867.html

例子

Acwing 203 同余方程为例,ax1 (modm) 可以写成 ax1=my,即 axmy=1。形式上等同于 ax+by=c, b=m, c=1

根据扩展欧几里得算法可以解出一个x0,那么根据通项解可知,就是在x0上加减若干个 bcm=m 。由于x0可能是负数,或者是大于m的正数,通过(x%b+b)%b使其成为(0,m)之间的正整数。

#include <cstdio>
using namespace std;

int exgcd(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int gcd = exgcd(b, a % b, x, y), temp = y;
    y = x - a / b * y;
    x = temp;
    return gcd;
}
int main() {
    int a, b, x, y;
    scanf("%d%d", &a, &b);
    exgcd(a, b, x, y);
    printf("%d\n", (x % b + b) % b);
    return 0;
}

其他还有

posted @   Yunchuan  阅读(361)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示
主题色彩