如何高效求解最大公约数与最小公倍数:从欧几里得到现代优化

1次阅读
没有评论

共计 1790 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

问题背景

最大公约数 (GCD) 和最小公倍数 (LCM) 在计算机科学和工程领域有着广泛的应用。例如:

如何高效求解最大公约数与最小公倍数:从欧几里得到现代优化

  • 密码学中 RSA 算法依赖大数 GCD 计算
  • 信号处理中的滤波器设计需要 LCM 确定采样周期
  • 游戏开发中碰撞检测使用 GCD 简化分数运算

算法对比

暴力解法

最直观的方法是枚举所有可能的公约数:

  1. 从较小数开始向下遍历
  2. 第一个能同时整除两数的即为 GCD

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

def gcd_brute(a, b):
    for i in range(min(a,b), 0, -1):
        if a % i == 0 and b % i == 0:
            return i

欧几里得算法

基于数学定理:gcd(a,b) = gcd(b, a mod b)

数学证明:

  1. 设 d = gcd(a,b),则 d | a 且 d |b
  2. a 可以表示为 b *q + r
  3. 因此 d |r,即 d 也是 b 和 r 的公约数
  4. 递归直到余数为 0,最后非零余数即为 GCD

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

def gcd_euclid(a, b):
    return a if b == 0 else gcd_euclid(b, a % b)

代码实现

Python 版本

def gcd(a, b):
    """递归实现欧几里得算法"""
    if b == 0:
        return abs(a)  # 处理负数
    return gcd(b, a % b)

def lcm(a, b):
    """利用 GCD 计算 LCM"""
    return abs(a * b) // gcd(a, b) if a and b else 0

if __name__ == "__main__":
    try:
        x, y = map(int, input().split())
        print(gcd(x, y), lcm(x, y))
    except ValueError:
        print("请输入两个整数")

Java 版本

import java.math.BigInteger;
import java.util.Scanner;

public class GCD {
    // 处理大数情况
    static BigInteger gcd(BigInteger a, BigInteger b) {return b.equals(BigInteger.ZERO) ? a.abs() : gcd(b, a.mod(b));
    }

    public static void main(String[] args) {Scanner sc = new Scanner(System.in);
        try {BigInteger x = sc.nextBigInteger();
            BigInteger y = sc.nextBigInteger();
            BigInteger gcd = gcd(x, y);
            BigInteger lcm = x.multiply(y).abs().divide(gcd);
            System.out.println(gcd + " " + lcm);
        } catch (Exception e) {System.out.println("输入格式错误");
        }
    }
}

性能优化

位运算加速

使用 Stein 算法(二进制 GCD 算法):

  1. 若 a 和 b 都是偶数,gcd(a,b)=2*gcd(a/2,b/2)
  2. 若 a 是偶数,b 是奇数,gcd(a,b)=gcd(a/2,b)
  3. 否则用更相减损术
def gcd_binary(a, b):
    if a == 0: return b
    if b == 0: return a
    shift = 0
    # 移除公共的 2 的因子
    while ((a | b) & 1) == 0:
        a >>= 1
        b >>= 1
        shift += 1
    # 确保 a 是奇数
    while (a & 1) == 0:
        a >>= 1
    # 主循环
    while b != 0:
        while (b & 1) == 0:
            b >>= 1
        if a > b:
            a, b = b, a
        b -= a
    return a << shift

避坑指南

处理特殊情况

  • 输入含负数:结果应始终为正
  • 输入为 0:LCM(0,x)=0,GCD(0,x)=|x|
  • 大数运算:使用 Java 的 BigInteger 避免溢出

扩展思考

计算多个数的 GCD

连续计算前两个数的 GCD,再与第三个数计算:

gcd(a,b,c) = gcd(gcd(a,b),c)

分布式计算

对于超大数(如 RSA-2048):

  1. 使用 Pollard’s Rho 算法分解质因数
  2. MapReduce 框架并行计算因数
  3. 合并各节点的部分结果

相关练习

  • LeetCode 1979. Find Greatest Common Divisor of Array
  • LeetCode 1819. Number of Different Subsequences GCDs
  • LeetCode 2543. Check if Point Is Reachable (超级 GCD 应用题)
正文完
 0
评论(没有评论)