CC++ & Algorithm

最大公约数与最小公倍数

困难0
语言版本:C++
概述:最大公约数是两个数最大的公共因数,最小公倍数是两个数最小的公共倍数,就像找两个班同学共同放假的日子。

最大公约数和最小公倍数:两个数的“公共因子”与“公共倍数”

今天我们来聊一对数学好兄弟——最大公约数(GCD)和最小公倍数(LCM)。
它们就像两个班级共同拥有的假期:最大公约数是最长的公共假期(相同因子中的最大者),最小公倍数则是下一次两个班同时放假的日子(相同倍数中的最小者)。
在编程竞赛和生活中,这两个概念经常一起出现,比如分糖果、安排班车、计算路灯同时亮起的时刻。掌握了它们,很多“周期”问题都会迎刃而解。


一、第一次见面:最大公约数(GCD)

最大公约数,英文叫 Greatest Common Divisor(GCD),是指两个或多个整数共有的因数中最大的那个。

因数的概念:一个整数能整除另一个整数,比如 12 ÷ 3 = 4,3 就是 12 的因数(也叫约数)。
公因数:两个数都有的因数。
最大公因数:公因数中最大的那一个。

例子:12 和 18

  • 12 的因数:1, 2, 3, 4, 6, 12
  • 18 的因数:1, 2, 3, 6, 9, 18
  • 公共因数:1, 2, 3, 6
  • 最大的是 6,所以 gcd(12, 18) = 6

生活场景:你有 12 颗奶糖和 18 颗水果糖,想平均分给几个好朋友,每人拿到同样多的奶糖和水果糖,最多可以分给几个人?
答案就是 gcd(12, 18) = 6,每人得到 2 颗奶糖和 3 颗水果糖。


二、再来认识:最小公倍数(LCM)

最小公倍数,英文叫 Least Common Multiple(LCM),是指两个或多个整数共有的倍数中最小的那个(0 除外)。

倍数的概念:一个整数乘以自然数得到的结果,比如 12 的倍数有 12, 24, 36, 48, …
公倍数:两个数共有的倍数。
最小公倍数:公倍数中最小的正数。

例子:12 和 18

  • 12 的倍数:12, 24, 36, 48, 60, …
  • 18 的倍数:18, 36, 54, 72, …
  • 公共倍数:36, 72, …
  • 最小的是 36,所以 lcm(12, 18) = 36

生活场景:红绿灯 A 每 12 秒变一次色,红绿灯 B 每 18 秒变一次色。它们同时变绿色之后,再过多少秒会再次同时变绿?
答案就是 lcm(12, 18) = 36 秒。


三、Python 直接计算:math 模块帮你搞定

Python 的 math 模块里已经提供了 gcd 函数,可以直接用。
至于最小公倍数,没有现成的函数,但有一个重要公式:

lcm(a, b) = a × b ÷ gcd(a, b)

注意:要先用整数乘法再做整除,避免出现小数。

import math

def lcm(a, b):
    """
    返回 a 和 b 的最小公倍数
    """
    return a * b // math.gcd(a, b)  # 先乘后整除,结果一定是整数

# 测试
num1 = 12   # 第一个数
num2 = 18   # 第二个数
print("gcd =", math.gcd(num1, num2))  # 输出 6
print("lcm =", lcm(num1, num2))       # 输出 36

四、自己动手写:辗转相除法(欧几里得算法)

虽然 math.gcd 很方便,但竞赛中有时需要自己实现 GCD。最经典的方法就是辗转相除法(也叫欧几里得算法)。

原理:两个整数 a 和 b(a ≥ b),它们的最大公约数等于 b 和 a % b 的最大公约数。重复这个操作,直到余数为 0,此时除的那个数就是答案。

例子:求 24 和 36 的 gcd

  • 36 ÷ 24 = 1 余 12 → 问题变成求 gcd(24, 12)
  • 24 ÷ 12 = 2 余 0 → 余数为 0,所以 gcd 是 12

代码实现(递归和循环两种写法):

# 递归写法
def gcd_recursive(x, y):
    """
    辗转相除法(递归)
    """
    if y == 0:
        return x            # 当 y 为 0 时,x 就是最大公约数
    return gcd_recursive(y, x % y)  # 交换位置并取余

# 循环写法(更推荐,不担心递归深度)
def gcd_loop(x, y):
    """
    辗转相除法(循环)
    """
    while y != 0:           # 直到 y 变成 0
        x, y = y, x % y     # 同时更新:x 变成 y,y 变成 x 除以 y 的余数
    return x                # 最终的 x 就是最大公约数

# 测试
print(gcd_recursive(12, 18))  # 输出 6
print(gcd_loop(12, 18))       # 输出 6

有了自己写的 gcd,也可以实现 lcm:

def lcm_custom(a, b):
    return a * b // gcd_loop(a, b)  # 使用上面自定义的 gcd

五、生活中的更多例子

  1. 分铅笔和橡皮:24 支铅笔和 36 块橡皮分给小朋友,每人得到的铅笔和橡皮一样多,最多分给多少人?
    答:gcd(24, 36) = 12,每人 2 支铅笔、3 块橡皮。

  2. 买零食凑整:A 零食每袋 6 元,B 零食每袋 10 元,小明想花同样多的钱买两种零食,最少要花多少钱?
    答:找 6 和 10 的最小公倍数 = lcm(6, 10) = 30 元(可以买 5 袋 A 或 3 袋 B)。

  3. 考试时间周期:数学考试每 3 天一次,英语考试每 5 天一次,今天两门都考了,下一次同时考是几天后?
    答:lcm(3, 5) = 15 天后。


六、新手容易犯的错误

  • 忘记 import math:直接写 math.gcd() 会报错,需要先 import math
  • lcm 公式写成 a * b / math.gcd(a, b)/ 会得到浮点数(比如 6.0),后续判断可能出错,建议用 // 整数除法。
  • 参数顺序误以为必须 a ≥ bgcd 函数内部会自动处理,即使 gcd(18, 12) 也能得到正确结果 6,但自己写循环时要注意迭代条件。
  • 未考虑 0gcd(0, a) 的结果是 alcm(0, a) 一般定义为 0(实际很少用到)。竞赛中题目通常保证输入为正整数。
  • 混淆 gcd 和 lcm 的含义:分糖果用 gcd,求下次同时发生用 lcm,多想想场景就不容易记反。

七、完整可运行的示例代码

下面是一个完整程序,用户输入两个正整数,输出它们的 gcd 和 lcm:

import math

def lcm(a, b):
    """
    计算 a 和 b 的最小公倍数
    """
    return a * b // math.gcd(a, b)  # 利用公式

# 输入两个数
num1 = int(input("请输入第一个正整数:"))  # 第一个数
num2 = int(input("请输入第二个正整数:"))  # 第二个数

# 计算并输出
g = math.gcd(num1, num2)       # 最大公约数
l = lcm(num1, num2)            # 最小公倍数

print("最大公约数 (GCD) =", g)   # 输出 gcd
print("最小公倍数 (LCM) =", l)   # 输出 lcm

运行示例

请输入第一个正整数:12
请输入第二个正整数:18
最大公约数 (GCD) = 6
最小公倍数 (LCM) = 36

八、相关知识点指引

  • 扩展欧几里得算法:不仅能求 gcd,还能找到满足 ax + by = gcd(a,b) 的整数解,在解模方程时很有用。
  • 分数化简:约分就是分子分母同时除以它们的 gcd。
  • 多个数的 gcd 与 lcm:可以两两逐步求出,比如 gcd(a,b,c) = gcd(gcd(a,b), c),lcm 同理。
  • 质因数分解法:另一种手算 gcd/lcm 的方法,适合小数字,但不适合编程(大数分解困难)。
  • CSP-J 数论常用技巧:在循环中求一组数的 gcd,或者利用 lcm 解决周期问题(比如“第几天同时休息”)。

掌握 gcd 和 lcm,就像掌握了一把钥匙,很多看似复杂的数学问题都能轻松打开。多动手写两次代码,你就是下一个数论小能手!

例题精讲

1单选题

已知两个正整数a和b,它们的最大公约数记为gcd(a,b)。下列哪个方法是计算gcd(a,b)最常用的算法?

A辗转相除法(欧几里得算法)
B枚举法
C二分法
D动态规划
2判断题

如果两个数互质,那么它们的最大公约数为1,最小公倍数为它们的乘积。

3填空题
以下Python函数使用递归实现辗转相除法求最大公约数,请补全代码。
def gcd(a, b):
    if b == 0:
        return ___
    else:
        return ___
4单选题

已知两个正整数x和y,它们的最大公约数是6,最小公倍数是120,且x=12,则y的值是多少?

A30
B40
C60
D72
5判断题

对于任意正整数a和b,gcd(a,b) * lcm(a,b) = a * b。