唯一分解定理——每个合数都能拆成质数积
困难16拆解数字的秘密——唯一分解定理(算术基本定理)
你有没有想过,每个数字就像是用一些“最小零件”拼起来的?比如数字 12,它可以写成 2 × 2 × 3,再比如 30 可以写成 2 × 3 × 5。而且你会发现,每个数字只能有一种拆法(顺序不算)。这就是唯一分解定理,也叫算术基本定理——它是数学里最重要的基石之一。
这个定理告诉我们:任何一个大于 1 的整数,要么本身就是质数,要么都能写成若干个质数相乘的形式,而且这种写法是唯一的(不考虑质数的顺序)。理解了这个定理,你就抓住了数字最底层的结构,以后学约分、求最大公约数、判断完全平方数都会变得很简单。
1. 拆数字就像拆乐高
每个合数(比如 12)都可以看成是用一些“质数积木”搭起来的。这些积木就是质数,比如 2、3、5、7……你只能用这些“最小零件”来拼出任何数字。
例如:
- 12 = 2 × 2 × 3(两块 2 积木和一块 3 积木)
- 18 = 2 × 3 × 3(一块 2,两块 3)
- 100 = 2 × 2 × 5 × 5
这种拆法只有一种,不会出现另一种完全不同的分解。比如 12,你永远找不到第二组质数相乘得到 12(除了把 2 和 3 顺序调换,但顺序不算)。
生活例子:班主任要把 24 块糖果平均分给几个小组,每个小组人数必须相同,且每组都是质数个同学。那么可能的分法是:2 人一组(每组 12 块)、3 人一组(每组 8 块)、4 人一组(每组 6 块)……但注意 4 不是质数,所以不能是 4 人一组。真正的质因数分解告诉我们:24 = 2 × 2 × 2 × 3,所以只有用 2 和 3 这两种质数来分组才能做到完全平分。
2. 怎么手动分解一个数?
我们想找出一个数的所有质因子,就像拆乐高一样,从最小的质数开始试:
- 从质数 2 开始,看看它能不能整除这个数。
- 如果能,就除一次,记录一个 2,然后用商继续试 2,直到不能整除为止。
- 接着试下一个质数 3,再试 5,7,11……直到除到剩下 1 为止。
重要技巧:你只需要试到 √n 就行(这里 √ 表示开平方)。为什么呢?因为如果 n 有一个比 √n 大的质因子,那么它一定只有一个(另外的因子必然比 √n 小),并且它就是最后剩下的那个数本身。比如 17 是质数,它大于 √17,却只能有一个因子就是它自己。
例子:分解 84
- 先用 2:84 ÷ 2 = 42,记下 2;42 ÷ 2 = 21,再记一个 2;21 不能被 2 整除。
- 换 3:21 ÷ 3 = 7,记下 3;7 不能被 3 整除。
- 换 5:7 不能被 5 整除(√7 ≈ 2.6,其实试到 2 就够了,因为 2 已经试过不行。严格来说试到 √7 即 2.6,但我们已经试过 2 了,下一个质数是 3,也试过不行,所以 7 是质数)。
- 最后剩下 7,把它记下来。结果:84 = 2 × 2 × 3 × 7。
3. 基础代码实现:用循环试除
下面是 C++ 的基础版本,直接按照上面的思路写:
#include <iostream>
using namespace std;
int main() {
int n; // 待分解的数字
cout << "请输入一个正整数:";
cin >> n;
cout << n << " = ";
for (int i = 2; i * i <= n; i++) { // i 从 2 开始试,只试到根号 n
while (n % i == 0) { // 只要能整除就一直除
cout << i; // 输出这个质因子
n /= i; // 更新 n 为商
if (n > 1) cout << " * "; // 如果还有剩下的因子,加乘号
}
}
if (n > 1) cout << n; // 最后剩下的质因子(可能等于 1 或一个大于根号 n 的质数)
cout << endl;
return 0;
}
运行示例:
输入 84 → 输出 84 = 2 * 2 * 3 * 7
输入 17 → 输出 17 = 17(因为 17 是质数,循环不执行,最后直接输出 n)
4. 代码进阶:封装成函数,更安全
把分解功能单独写成函数,这样更容易在多个地方使用。注意每行变量定义都加上了中文注释。
#include <iostream>
#include <vector> // 用向量存储因子
using namespace std;
// 函数:返回一个数的所有质因子(按从小到大顺序)
vector<int> factorize(int num) {
vector<int> factors; // 用来存放质因子的数组
int n = num; // 复制一份,避免修改原值
for (int i = 2; i * i <= n; i++) { // i 是当前试除的质数
while (n % i == 0) { // 如果能整除
factors.push_back(i); // 把 i 加入列表
n /= i; // 除以 i
}
}
if (n > 1) factors.push_back(n); // 最后剩下的大于1的也是质因子
return factors;
}
int main() {
int number; // 要分解的整数
cout << "请输入一个正整数:";
cin >> number;
vector<int> result = factorize(number); // 调用函数得到质因子列表
cout << number << " = ";
for (int i = 0; i < result.size(); i++) {
cout << result[i];
if (i != result.size() - 1) cout << " * "; // 除了最后一个,后面都加乘号
}
cout << endl;
return 0;
}
优点:函数返回一个 vector,你可以用它做更多操作,比如统计每个质因子出现的次数,或者判断是否是质数(如果结果只有自己本身)。
5. 新手容易犯的错误
- 忘记处理最后的质因子:循环结束后,如果 n 还大于 1,它一定是质数(比如输入 17,循环一次都不执行,n 还是 17),必须输出它。很多初学者只输出循环内的因子,导致质数的情况只输出空。
- 循环条件写错:写成
i <= n会导致循环次数过多,效率极低,而且如果 n 本身是质数,循环会一直试到 n,浪费大量时间。写成i * i <= n才是正确的。 - 忘记更新 n 的值:在 while 循环里要执行
n /= i,否则会无限循环(因为 n 一直不变,永远被 i 整除)。 - 输出格式混乱:比如最后一个乘号多输出,或者没有乘号。上面的代码用
if (n > 1) cout << " * ";来控制,稳妥。 - 不理解试除范围:为什么只试到根号 n?因为如果 n 有因子 a 和 b(a ≤ b),那么 a ≤ √n,b ≥ √n。只要试到 √n,就能找到所有小因子,剩下的就是大因子(如果有,一定是质数)。
6. 生活中的例子
- 分班游戏:学校要把 60 个学生平均分成几个小组,每组人数一样且是质数。你能有几种分法?用唯一分解定理:60 = 2 × 2 × 3 × 5,所以可能的每组人数是 2、3、5、2×2=4(但 4 不是质数!)、2×3=6(也不是质数)、2×5=10(不是)、3×5=15(不是)、2×2×3=12(不是)……等等。实际上只有质数本身(2,3,5)可以作为每组人数,因为只有质数才能保证组数也是整数且每组人数为质数。所以可以分成 2 人一组(30 组)、3 人一组(20 组)、5 人一组(12 组)。
- 排队问题:学校有 72 名同学要排成方阵(每行人数相同且为质数),请问每行最少排几人?72 = 2×2×2×3×3,它的质因子只有 2 和 3,所以可能的每行人数是 2、3、2×2=4(不是质数)、2×3=6、2×2×2=8、3×3=9、2×2×3=12……只有 2 和 3 是质数,所以可以排 2 人一行(36 行)或 3 人一行(24 行)。最少的是 2 人一行。
7. 唯一分解定理有什么用?
这个定理是数论的基石,很多问题都离不开它:
- 求最大公约数(GCD):比如求 12 和 18 的最大公约数。12=2²×3,18=2×3²,取每个质因子的最小指数:2¹×3¹ = 6。
- 求最小公倍数(LCM):取每个质因子的最大指数:2²×3² = 36。
- 判断完全平方数:一个数是完全平方数当且仅当它的每个质因子的指数都是偶数。比如 36 = 2²×3²,指数都是偶数,所以 36 是完全平方数;而 12 = 2²×3¹,3 的指数是奇数,所以不是。
- 判断质数:如果一个数只有一种分解方式(即只有它本身),它就是质数。
- 简化分数:约分就是去掉分子分母公共的质因子。
8. 总结与相关学习指引
唯一分解定理告诉我们:每个数字的内部结构是唯一确定的。掌握了分解方法,你就能理解数字的“DNA”。接下来可以学习:
- 质数判断:如何快速判断一个数是不是质数(试除法配合根号 n)。
- 筛法(埃氏筛、欧拉筛):一次性找出一个范围内的所有质数。
- 最大公约数与最小公倍数:用质因数分解法或辗转相除法。
- 完全平方数与完全立方数:用质因子指数判断。
试着用今天学到的知识,分解一下你的生日日期(比如 2009 年 5 月 15 日,数字 2009515),看看它有哪些质因子?动手写代码试一试吧!
例题精讲
根据唯一分解定理,以下哪个数分解为质因数的结果与其他选项不同?(假设按从小到大顺序排列)
以下哪个选项不是60的质因数分解?
根据唯一分解定理,100的质因数分解结果可以写成2²×5²,也可以写成5²×2²,但写成2×5×2×5也是正确的。
以下C++函数用于输出整数n(n≥2)的质因数分解结果(从小到大,重复因子重复输出)。例如输入12,输出"2 2 3"。请补全代码。
void factorize(int n) {
for (int i = 2; i * i <= n; i++) {
while (___1___) {
cout << i << " ";
___2___;
}
}
if (___3___) {
cout << n;
}
}以下C++函数用于计算整数n(n≥2)所有不同质因子的个数(例如12的不同质因子有2和3,返回2)。请补全代码。
int countPrimeFactors(int n) {
int cnt = 0;
for (int i = 2; i * i <= n; i++) {
if (___1___) {
cnt++;
while (___2___) {
___3___;
}
}
}
if (n > 1) cnt++;
return cnt;
}