CC++ & Algorithm

最大公约数(GCD)与最小公倍数(LCM)——找到数字的共同“因数”

中等2
语言版本:C++
概述:理解两个数的最大公约数和最小公倍数的含义,并用C++编写程序计算它们。

最大公约数和最小公倍数——用C++找出数字的共同“因数”

同学们,你有没有遇到过这样的问题:妈妈有两根绳子,一根长24厘米,一根长36厘米,她想把两根绳子都剪成一样长的短绳,而且每段尽可能长,不剩零头。每段最长能剪多长?这个“最长”就是最大公约数(也叫最大公因数)。反过来,如果她想用这两种绳子拼成同样长的长绳,拼出来的绳子要刚好是整数段,而且尽可能短,这个最短长度就是最小公倍数

这两个概念不仅在生活中有用,在数学里也非常重要,比如分数化简、解应用题,甚至在C++竞赛中也经常用到。我们这就来彻底搞懂它们,并学会用C++程序一键计算。


一、什么是最大公约数(GCD)?

定义:两个整数能同时整除的最大正整数。
例如:24和36,能同时整除24和36的数有1、2、3、4、6、12,其中最大的是12,所以12就是它们的最大公约数,写作 gcd(24,36)=12。

生活例子

  • 你有12块巧克力,你有朋友有18块巧克力,你们想平均分给几个小朋友,每个小朋友分到的巧克力数量相同,而且不拆开整块。最多可以分给几个小朋友?答案是 gcd(12,18)=6,每个小朋友得到2块和3块。
  • 学校合唱队男生24人,女生36人,要分成人数相等的队伍(每队男女生人数相同),最多能分几队?也是12队,每队2男3女。

C++中的辗转相除法(欧几里得算法)
这是求最大公约数最经典的方法:

  1. 用较大的数除以较小的数,得到余数。
  2. 把除数变成新的被除数,余数变成新的除数。
  3. 重复直到余数为0,此时的除数就是最大公约数。

例如求 gcd(24,36):

  • 36 ÷ 24 = 1 余 12
  • 24 ÷ 12 = 2 余 0 → 除数12就是答案。

来看代码:

#include <iostream>
using namespace std;

// 辗转相除法求最大公约数
int gcd(int a, int b) {
    while (b != 0) {           // 当除数不为0时继续
        int temp = a % b;      // temp 存余数
        a = b;                 // 原来的除数变成新的被除数
        b = temp;              // 余数变成新的除数
    }
    return a;                  // 最后被除数就是最大公约数
}

注意:如果a或b是负数,这个算法也能工作,但通常我们只讨论正整数。可以在函数开始加上 a = abs(a); b = abs(b); 来取绝对值。


二、什么是最小公倍数(LCM)?

定义:两个整数能同时整除它们的最小正整数。
例如:4和6,能同时被4和6整除的数有12、24、36……其中最小的是12,所以12就是最小公倍数,写作 lcm(4,6)=12。

生活例子

  • 小明每4天去一次图书馆,小红每6天去一次,他们某天在图书馆相遇,下一次相遇至少多少天后?答案是 lcm(4,6)=12天。
  • 你想用两种贴纸装饰班级墙报:一种长24厘米,一种长36厘米,把它们首尾相连拼成一样长的装饰条,最短需要多长?答案是 lcm(24,36)=72厘米。

公式巧算
两个数的最小公倍数 = 两数乘积 ÷ 它们的最大公约数。
即:lcm(a,b) = a * b / gcd(a,b)

但注意:a * b 可能会很大,导致整数溢出(比如a=1000000, b=1000000时乘积会超出int范围)。所以正确写法是先除后乘
lcm(a,b) = a / gcd(a,b) * b
这样先做除法,结果一定是个整数,再乘b就安全多了。

// 最小公倍数,先除后乘防止溢出
int lcm(int a, int b) {
    return a / gcd(a, b) * b;   // 先除以最大公约数,再乘另一个数
}

三、新手容易犯的错误

  1. 忘记处理零:如果a或b为0,gcd会怎样?按照数学定义,gcd(0, n)=n。但代码中如果b=0,while循环直接跳过,返回a,刚好就是n。但如果a=0且b!=0,循环第一次 temp = 0 % b = 0,然后a=0,b=0,最终返回0。实际上gcd(0,n)=n,所以最好在函数开头判断:if (a == 0) return b; if (b == 0) return a;

  2. LCM溢出:直接用 a * b / gcd(a,b),如果a和b都接近int最大值,乘积会溢出变成负数。一定要先除后乘

  3. 循环条件写错:新手可能写成 while (b > 0),但C++中非0即为真,写成 while (b) 也行,但 while (b != 0) 更清晰。注意不能用 while (a % b != 0),因为循环体里a和b会变化。

  4. 负数处理:如果用户输入了负数,gcd会返回负数?比如gcd(-24,36) = ? 标准库函数通常返回正数,但我们的代码会输出-12。可以在函数开始取绝对值。


四、完整示例代码(带详细注释)

下面是一个完整的C++程序,包括输入、计算和输出。注意每一行变量定义都加了中文注释。

#include <iostream>
#include <cmath>      // 为了使用abs()取绝对值
using namespace std;

// 函数1:辗转相除法求最大公约数
int gcd(int a, int b) {
    a = abs(a);          // 取绝对值,保证结果为正
    b = abs(b);
    // 处理0的情况:0和任何数的gcd是另一个数
    if (a == 0) return b;
    if (b == 0) return a;
    
    while (b != 0) {               // 当除数不为0时循环
        int remainder = a % b;     // remainder 保存余数
        a = b;                     // 原来的除数变成新被除数
        b = remainder;             // 余数变成新除数
    }
    return a;                      // 循环结束,a就是最大公约数
}

// 函数2:利用公式求最小公倍数(先除后乘防溢出)
int lcm(int a, int b) {
    a = abs(a);
    b = abs(b);
    if (a == 0 || b == 0) return 0; // 0没有倍数,特殊处理
    return a / gcd(a, b) * b;       // 先除后乘
}

int main() {
    int x, y;                        // 定义两个整数x和y
    cout << "请输入两个整数(用空格隔开): ";
    cin >> x >> y;

    int g = gcd(x, y);               // 计算最大公约数
    int l = lcm(x, y);               // 计算最小公倍数

    cout << "最大公约数 (GCD): " << g << endl;
    cout << "最小公倍数 (LCM): " << l << endl;

    return 0;
}

运行示例
输入:24 36
输出:

最大公约数 (GCD): 12
最小公倍数 (LCM): 72

输入:0 15
输出:

最大公约数 (GCD): 15
最小公倍数 (LCM): 0

五、延伸学习——更多求GCD的方法

除了辗转相除法,还有更相减损术(中国古代《九章算术》中的方法):

  • 用大数减小数,然后差和较小数继续相减,直到两数相等。
  • 例如:gcd(24,36):36-24=12 → 24-12=12 → 12=12 → 答案为12。
  • 但减法次数多,效率低,所以转辗相除法(取模)更常用。

另外,C++标准库 <algorithm> 里有一个 __gcd 函数(注意是双下划线,非标准),在竞赛中可以直接使用:

#include <algorithm>
int g = __gcd(24, 36);

不过为了更好的可移植性,自己写函数更稳妥。


六、相关知识点

  • 质因数分解:最大公约数和最小公倍数也可以通过对每个数分解质因数来求,例如24=2³×3,36=2²×3²,取公共指数小的得gcd=2²×3=12,取指数大的得lcm=2³×3²=72。
  • 分数化简:用gcd给分子分母同时除以最大公约数,得到最简分数。
  • 扩展欧几里得算法:不仅能求gcd,还能求出一组整数解,用于解不定方程(比如密码学中的RSA算法)。
  • 多个数的gcd/lcm:可以两两递归计算,例如 gcd(a,b,c) = gcd(gcd(a,b), c)

希望这些内容能帮你牢牢掌握最大公约数和最小公倍数,下次遇到切绳子、拼积木的问题,就能用程序轻松解决啦!

例题精讲

1单选题

已知两个正整数a和b,它们的最大公约数(GCD)是d,最小公倍数(LCM)是m。下列哪个关系式是正确的?

Am = a * b / d
Bm = d / (a * b)
Cm = a + b - d
Dm = (a * b) * d
2判断题

使用辗转相除法(欧几里得算法)求两个数的最大公约数时,如果其中一个数为0,则另一个数就是它们的最大公约数。

3填空题
以下函数使用递归实现求最大公约数,请补充完整。

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, ___);
}
4单选题

假设已有计算最大公约数的函数 int gcd(int a, int b); 下列哪个函数正确实现了计算两个整数的最小公倍数?

Aint lcm(int a, int b) { return a * b / gcd(a, b); }
Bint lcm(int a, int b) { return gcd(a, b) / (a * b); }
Cint lcm(int a, int b) { return a * b * gcd(a, b); }
Dint lcm(int a, int b) { return (a + b) / gcd(a, b); }
5判断题

对于任意两个正整数a和b,都有 a * b = GCD(a, b) * LCM(a, b)。