🔄

辗转相除法

两个数玩追逐游戏,最后留下的就是最大公约数

💡 打个比方

求 48 和 18 的最大公约数,就像两个数玩追逐游戏:大数除以小数,把"余下的部分"继续拿去追。

48 ÷ 18 余 12 → 18 ÷ 12 余 6 → 12 ÷ 6 余 0。余数越来越小,最后除数为 0 的那一刻,除数 6 就是最大公约数。

换个角度看:用同一种正方形瓷砖去铺 48×18 的地砖,不留缝、不切割,能用的最大瓷砖边长,恰好也是 6。

欧几里得算法(辗转相除法):gcd(a, b) = gcd(b, a % b),余数为 0 时返回除数。为什么对?能同时整除 a 和 b 的数,也一定能整除余数 a % b,所以公约数"追"着余数走,一个都不会丢。

🖼️ 大图看明白

① 追逐游戏:余数越来越小,最后非零余数就是 GCD 48 ÷ 18 余 12 18 ÷ 12 余 6 12 ÷ 6 余 0 GCD = 6 ② 铺砖视角:48×18 的矩形,能铺的最大正方形瓷砖边长 = GCD 18×18 18×18 12×12 6×6 6×6 先铺 18×18,剩 12×18 再铺 12×12,剩 12×6 再铺 6×6——铺不动了,6 就是最大公约数

📏 辗转相除,只要 3 步

  1. 第 1 步:大数除以小数,取余数。比如 48 % 18 = 12。
  2. 第 2 步:角色互换。小数(18)变成新的被除数,余数(12)变成新的除数:18 % 12 = 6。重复第 1 步。
  3. 第 3 步:余数为 0 就停。谁当除数、余数为 0,谁就是最大公约数。12 % 6 = 0,所以 GCD = 6。
为什么一定会停?余数 b > a % b 严格越来越小(且都是非负整数),一路减小必然到 0,不可能无限追下去。

💻 C++ 怎么写

// 辗转相除法:递归版
int gcd(int a, int b) {
    if (b == 0) return a;      // 余数为 0,a 就是答案
    return gcd(b, a % b);      // 角色互换,继续追
}

int main() {
    int a = 48, b = 18;
    cout << gcd(a, b) << "\n";          // 6
    // 最小公倍数 = a ÷ GCD × b(先除后乘防溢出)
    cout << a / gcd(a, b) * b << "\n";  // 144
    return 0;
}
考点提醒:写 LCM 用 a / gcd * b 而不是 a * b / gcd,因为 a × b 可能超过 int 范围;先除一定整除,再乘就安全了。

✋ 你来试试

题 1:用辗转相除法求 105 和 35 的最大公约数。
105 % 35 = 0,一步就余 0 了!余数为 0 时,除数 35 就是答案:GCD = 35。这也是为什么"小数能整除大数"时,GCD 直接就是小数。
题 2:为什么求 LCM 要写 a / gcd * b,而不是 a * b / gcd?
因为 a × b 可能大得超出 int 的 21 亿上限(比如 a、b 都是几百万时)。而先做 a / gcd 一定整除,得到的数比 a 小,再乘 b 就不容易溢出。两个式子数学上相等,写法不同,安全性差很多。
🎯 一句话记住:辗转相除 = 大数追小数、余数越来越小,余 0 时除数就是 GCD;LCM 用 a ÷ GCD × b,先除后乘防溢出。