CC++ & Algorithm

C++最小公倍数(LCM)

困难21
语言版本:C++Python
概述:什么是最小公倍数,以及如何用C++计算两个数的最小公倍数。

最小公倍数(LCM)详解:从生活例子到C++实现

什么是最小公倍数?

两个数的最小公倍数(Least Common Multiple,简称LCM)就是能同时被这两个数整除的最小的正整数。简单来说,就是两个数的“公共倍数”中最小的那个。

举个例子:你和朋友一起跑步,你跑一圈要4分钟,他跑一圈要6分钟。你们同时从起点出发,问多少分钟后你们会再一次同时回到起点?这个时间就是4和6的最小公倍数。因为4的倍数是4、8、12、16……,6的倍数是6、12、18……,共同的倍数里最小的是12,所以答案是12分钟。

再举个生活中的例子:妈妈买了一些糖果,想平均分给小明和小红,每人分的数量必须都是整数。小明每次可以拿4颗,小红每次可以拿6颗。那么妈妈至少需要准备多少颗糖果,才能让两人都正好拿完?答案也是12颗(小明拿3次,小红拿2次)。

最小公倍数和最大公约数的关系

要计算最小公倍数,有一个非常方便的公式:

LCM(a,b)=a×bGCD(a,b)\text{LCM}(a, b) = \frac{a \times b}{\text{GCD}(a, b)}

其中 GCD 是最大公约数(Greatest Common Divisor)。这个公式的意思是:两个数的乘积等于它们的最小公倍数乘以最大公约数。比如4和6:最大公约数是2,乘积是24,24 ÷ 2 = 12,正好是最小公倍数。

为什么这个公式成立?因为每个数都可以分解成质因数,最小公倍数取所有质因数的最高次幂,最大公约数取所有质因数的公共最低次幂,两者乘起来正好回原两个数的乘积。不过你暂时不用深究,记住这个常用公式就够了。

如何用C++计算最小公倍数?

既然有公式,我们只需要先算出最大公约数,然后代入公式即可。最大公约数可以用之前学过的“辗转相除法”(也称欧几里得算法)来求。代码非常简洁。

关键点:防止计算溢出

注意公式里先乘后除:a * b / gcd(a, b)。如果a和b比较大,比如a=1000000,b=1000000,那么a * b = 1e12,已经超过了C++中int类型的范围(int最大约21亿)。虽然结果在long long范围内,但乘出来的中间结果会溢出。所以一定要先除后乘a / gcd(a, b) * b,这样中间结果不会太大。另外,为了保险,通常将函数返回值类型设为long long

常见错误与避免方法

  1. 忘记处理a或b为0的情况
    如果其中一个数为0,最小公倍数数学上没有定义(或定义为0)。实际编程中,如果输入可能是0,需要先判断。但通常我们只处理正整数,可以忽略。

  2. 直接使用int类型导致溢出
    刚才已经说过,一定要用long long或先除后乘。

  3. 调用gcd时参数顺序影响结果
    辗转相除法对任意顺序都有效,但最好养成习惯:gcd(a, b)中如果a<b,算法会自动交换,没问题。

  4. 未包含头文件
    #include <iostream>using namespace std; 别忘了。

完整可运行代码示例

下面是完整的C++程序,包含了gcd函数和lcm函数,以及一个简单的交互界面。代码中每行变量都加了中文注释,方便理解。

#include <iostream>
using namespace std;

// 计算最大公约数(欧几里得算法)
int gcd(int a, int b) {
    while (b != 0) {
        int temp = a % b;  // 临时变量保存余数
        a = b;             // 更新a为原来的b
        b = temp;          // 更新b为余数
    }
    return a;
}

// 计算最小公倍数(先除后乘防止溢出)
long long lcm(int a, int b) {
    // 先计算 a / gcd(a, b),再乘以 b,中间结果不会太大
    return (long long)a / gcd(a, b) * b;
}

int main() {
    int x, y;  // 两个输入的正整数
    cout << "请输入两个正整数:";
    cin >> x >> y;

    // 计算并输出最小公倍数
    long long result = lcm(x, y);
    cout << "最小公倍数是:" << result << endl;

    return 0;
}

运行示例:

请输入两个正整数:4 6
最小公倍数是:12

输入12和18:

请输入两个正整数:12 18
最小公倍数是:36

更多思考:当两个数很大时怎么办?

如果两个数都达到几亿,它们的乘积会非常大,long long也可能装不下。这时先除后乘依然有效,因为a / gcd(a, b)会先缩小一个数。但如果你用的是更大的整数类型(比如unsigned long long),依然要注意极限情况。对于竞赛或实际应用,通常题目会指定范围,你按范围选择合适类型即可。

相关知识点指引

  • 最大公约数(GCD):最小公倍数的好朋友,用辗转相除法可以轻松求得。
  • 质因数分解:可以手动求LCM,但不适合计算机直接算(大数分解很慢)。
  • 多个数的最小公倍数:可以先求两个数的LCM,再用结果和第三个数求LCM,以此类推。
  • 数据范围与类型:学习intlong longunsigned long long的区别,避免溢出。

希望这篇文章能帮你彻底搞懂最小公倍数!下次遇到“多久再次相遇”、“至少分多少”这类问题,就可以自己写程序算啦。

例题精讲

1单选题

已知两个正整数 a=12,b=18,它们的最大公约数 gcd(12,18)=6,那么它们的最小公倍数是多少?

A12
B18
C36
D6
2单选题

在C++中计算两个整数a和b的最小公倍数时,为了避免中间结果溢出(例如a和b都很大),推荐使用下列哪种写法?(假设gcd函数已正确实现)

Areturn a * b / gcd(a, b);
Breturn a / gcd(a, b) * b;
Creturn gcd(a, b) / a * b;
Dreturn a * b * gcd(a, b);
3判断题

如果两个正整数互质(即最大公约数为1),那么它们的最小公倍数等于这两个数的乘积。

4填空题
以下函数用于计算两个正整数 a 和 b 的最小公倍数(假设 gcd 函数已实现)。请补全函数体。

int lcm(int a, int b) {
    return ___;
}
5填空题
以下代码用于计算三个正整数 a、b、c 的最小公倍数。函数 lcm(a,b) 已实现(计算两个数的最小公倍数)。请补全 lcm3 函数体。

int lcm(int a, int b) {
    return a / gcd(a, b) * b;
}

int lcm3(int a, int b, int c) {
    return lcm(___, c);
}