完全数是等于其所有真因子(即小于自身的正约数)之和的正整数;例如6的真因子为1、2、3,和为6;1不是完全数(无真因子),枚举时需排除自身、避免重复计数平方根,最直接判断法是遍历1到n−1累加能整除的数并比对。

什么是完全数,以及判断逻辑的边界条件
完全数是指一个正整数等于它所有真因子(即小于它本身的正因数)之和。比如 6 的真因子是 1、2、3,而 1 + 2 + 3 == 6,所以它是完全数。
注意几个关键点:
-
1不是完全数(它没有真因子,和为 0) - 必须严格排除自身,
i == n不能参与求和 - 因子成对出现,但平方根只算一次,比如
sqrt(16) == 4,只加一次 4
用 for 循环暴力枚举真因子并累加
最直接的方式是从 1 枚举到 n - 1,逐个检查是否能整除:
bool isPerfect(int n) {
if (n <= 1) return false;
int sum = 0;
for (int i = 1; i < n; ++i) {
if (n % i == 0) sum += i;
}
return sum == n;
}
这种方式简单清晰,但时间复杂度是 O(n),对大数(如接近 INT_MAX)会明显变慢。实际测试中,isPerfect(33550336) 就会卡住。
立即学习“C++免费学习笔记(深入)”;
优化:只遍历到 sqrt(n),避免超时
利用因子对称性:若 i 是 n 的因子,那么 n / i 也是。我们只需遍历 i 从 1 到 sqrt(n):
- 若
i * i == n,说明是平方数,i只加一次 - 若
i != 1且i < n,则n / i是另一个真因子,但要确保n / i < n(即i > 1自动满足)
bool isPerfect(int n) {
if (n <= 1) return false;
int sum = 1; // 1 总是真因子(除了 n == 1)
int i = 2;
for (; i * i < n; ++i) {
if (n % i == 0) {
sum += i;
sum += n / i;
}
}
if (i * i == n && i < n) sum += i; // 平方根,且不能等于 n(即 n != 1)
return sum == n;
}
注意:
- 初始化
sum = 1,跳过i = 1的循环处理 -
i * i < n避免浮点误差和sqrt的类型转换问题 - 最后检查
i * i == n时,必须加&& i < n,否则n == 1会误触发
已知完全数极少,可直接查表验证小范围值
目前已知的偶完全数都形如 2^(p−1) × (2^p − 1),其中 2^p − 1 是梅森素数。前五个完全数是:
628496812833550336
对 int 范围内(≤ 2147483647)的输入,只需要比对这 5 个数:
bool isPerfect(int n) {
static const int perfects[] = {6, 28, 496, 8128, 33550336};
for (int p : perfects) {
if (n == p) return true;
if (n < p) break;
}
return false;
}
这个版本常数时间、零出错风险,适合嵌入性能敏感或输入范围明确的场景。但别忘了:目前尚未发现奇完全数,也未证明其不存在——所以该表只覆盖所有已知偶完全数,且仅限 int 能表示的范围。
真正容易被忽略的是:n == 1 的边界处理,以及平方根重复累加的条件判断。哪怕只漏掉一个 && i < n,在 n == 4 时就会错误返回 true(因为把 2 加了两次,或误把 4 当作真因子)。


















