CC++ & Algorithm

整数唯一分解定理——把数字拆成质因子的积木

中等2
语言版本:C++
概述:学习任何一个大于1的整数都可以唯一地分解成质数的乘积,并用C++编写质因数分解程序。

拆解数字的积木:整数唯一分解定理与质因数分解

什么是整数唯一分解定理?

你有没有玩过积木?每一种积木形状固定,用它们可以拼出各种各样的造型。在数学里,质数就像是“数字积木”,而整数唯一分解定理告诉我们:任何一个大于1的整数,都可以被拆成几个质数相乘的形式,而且只有一种拆法(不考虑乘的顺序)。比如:

  • 12 = 2 × 2 × 3
  • 100 = 2 × 2 × 5 × 5
  • 30 = 2 × 3 × 5
  • 7 = 7(它本身已经是质数)

这个定理也叫算术基本定理。它是数论的基石,很多问题都要用到它,比如求最大公约数、最小公倍数、约数个数等等。学会了它,你就能把任何数字“拆开看本质”!

质因数是什么?

质因数就是那些组成一个合数的质数。比如12的质因数是2和3,其中2出现了两次。注意:1不是质数,也不是合数,所以不在分解范围内。

我们可以把分解过程想象成“切蛋糕”:把一个数字不断切成质数小块,直到切不动为止。

试除法——如何把数字拆开?

最直观的方法叫试除法:从最小的质数2开始,不断用当前数去除以这个质数,如果能整除,就把它记下来,然后继续除;直到不能整除,再换下一个质数3、5、7……一直试到被除数变成1。

这个方法的原理很简单:如果n能被某个数k整除,那么k就是n的一个因子。如果k是质数,那它就是质因子;如果k是合数,它会被更小的质因子分解掉。所以从小到大试,保证每次找到的都是质因子。

为什么只需要试到√n?

这是一个重要的优化:如果n有一个大于√n的质因子,那么它一定还有一个小于√n的质因子与之配对(因为两个因子相乘等于n)。所以当我们试完所有小于等于√n的数后,如果n还没有变成1,那么剩下的那个数一定是一个大于√n的质数(也就是n本身)。这样就不用试到n了,大大节省时间。

举个例子:分解101。√101≈10,我们试2、3、5、7,都不能整除,说明101没有小于等于10的因子,所以101就是质数。直接输出它。

生活里的例子

  • 零花钱:你有60元零花钱,想平均分给朋友们(每人分一样多且必须是整数元)。你可以先分解60 = 2×2×3×5,那么可能的份数就是这些质因子的组合:1元、2元、3元、4元、5元、6元、10元、12元、15元、20元、30元、60元。每一种恰好对应一个约数。
  • 排队:学校组织100人做广播操,要求每排人数相同,那么可能的排数就是100的约数。100 = 2² × 5²,所以排数可以是1,2,4,5,10,20,25,50,100。

C++实现:完整代码与逐行注释

下面的程序读取一个大于1的整数,输出它的质因数分解式。

#include <iostream>
using namespace std;

// 函数:对整数n进行质因数分解,输出结果
void factorize(int n) {
    cout << n << " = ";
    
    // 第一步:处理所有的因子2(因为2是唯一的偶质数)
    int count = 0;                   // 计数器,用于输出乘号
    while (n % 2 == 0) {
        if (count > 0) cout << " × "; // 非第一个因子前加乘号
        cout << 2;
        n /= 2;                      // 除以2,缩小问题
        count++;
    }
    
    // 第二步:从3开始,只试奇数(因为偶因子已经被2处理了)
    // 循环条件:i*i <= n 等价于 i <= sqrt(n)
    for (int i = 3; i * i <= n; i += 2) {
        while (n % i == 0) {
            if (count > 0) cout << " × ";
            cout << i;
            n /= i;
            count++;
        }
    }
    
    // 第三步:如果n > 1,说明剩下的也是一个质数(且大于sqrt(原数))
    if (n > 1) {
        if (count > 0) cout << " × ";
        cout << n;
    }
    cout << endl;
}

int main() {
    int num;                         // 用户输入的数
    cout << "请输入一个大于1的整数: ";
    cin >> num;
    if (num > 1) {
        factorize(num);
    } else {
        cout << "输入无效,请确保输入大于1。" << endl;
    }
    return 0;
}

运行效果

请输入一个大于1的整数: 60
60 = 2 × 2 × 3 × 5
请输入一个大于1的整数: 97
97 = 97

常见错误与坑

  1. 忘记处理最后剩下的质数:循环结束后,如果n还是大于1,说明它本身就是一个质因子,必须输出。很多新手只处理了循环内的部分,漏掉了最后的质数。
  2. 试除范围搞错:有人会试到n-1,那样效率极低;或者分不清“i*i<=n”和“i<=sqrt(n)”的写法。注意用i*i <= n可以避免浮点误差,也更快。
  3. 忘记区分质数和合数:分解过程中,如果n本身是质数,循环一次都不会进入,最后直接输出n即可。代码中已经处理了这个情况。
  4. 变量命名不清晰:代码中变量n会不断变化,容易搞混。建议保持原有变量名,但注意在循环中它代表当前剩余的数。
  5. 输入0或1:0和1不能进行质因数分解,程序必须先判断。

完整示例(不同的输入)

输入输出
22 = 2
1212 = 2 × 2 × 3
100100 = 2 × 2 × 5 × 5
144144 = 2 × 2 × 2 × 2 × 3 × 3
997997 = 997(质数)
999999 = 3 × 3 × 3 × 37

相关知识点指引

学会了质因数分解,你可以进一步学习:

  • 最大公约数(GCD):把两个数分解后,取公共质因子的最小次数相乘。
  • 最小公倍数(LCM):取所有质因子的最大次数相乘。
  • 约数个数:如果n = p₁^a₁ × p₂^a₂ × ... × pₖ^aₖ,那么约数个数为 (a₁+1)(a₂+1)...(aₖ+1)。
  • 约数之和:也有公式,用等比数列求和。
  • 欧拉函数:求小于n且与n互质的数的个数,也要先分解n。
  • 质数筛法:如果我们需要快速分解多个数,可以先用筛法求出质数表,再分解。

整数唯一分解定理就像一把“数字手术刀”,帮你拆开任何合数。动手写代码试试,把课本上的数字都分解一遍吧!

例题精讲

1单选题

根据整数唯一分解定理,下列哪个选项是整数60正确的质因数分解?

A2×2×3×5
B2×3×10
C4×3×5
D2×6×5
2单选题

关于整数唯一分解定理,下列说法正确的是?

A每个大于1的整数都可以分解为质因数的乘积,且不考虑顺序时分解唯一。
B每个大于1的整数都可以分解为质因数的乘积,但分解不唯一。
C质数无法分解为质因数乘积。
D合数只能分解为两个质数的乘积。
3判断题

根据整数唯一分解定理,正整数n的质因数分解中,质因数出现的顺序不同,则视为不同的分解。

4判断题

如果两个大于1的整数的质因数分解(按从小到大排序)完全相同,那么这两个数相等。

5填空题
以下函数用于输出正整数n的质因数分解(例如n=60输出"2*2*3*5"),请补全代码。
void factor(int n) {
    for (int i = 2; i * i <= n; i++) {
        while (n % i == 0) {
            cout << i;
            if (n != i) cout << '*';
            ___ ;
        }
    }
    if (n > 1) cout << n;
}