完美数如何只枚举到平方根判断因子和?(LeetCode 507)
简化版
完美数是等于除自身外所有正因子之和的数。判断时先把 sum 设为 1,然后枚举 i 从 2 到 sqrt(n):如果 i 是因子,就同时加入 i 和 n / i。最后看 sum == n。注意 n <= 1 不是完美数。
详细版
boolean checkPerfectNumber(int num) {
if (num <= 1) return false;
int sum = 1;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
sum += i;
if (i != num / i) sum += num / i;
}
}
return sum == num;
}
例如 28 的真因子是 1,2,4,7,14,和为 28,所以是完美数。枚举到平方根即可,因为因子总是成对出现。
完整版教学
一、完美数的定义
完美数要求:
num = 所有真因子之和
真因子是不包含数字自身的正因子。例如:
28 的真因子:1, 2, 4, 7, 14
1 + 2 + 4 + 7 + 14 = 28
所以 28 是完美数。
二、为什么从 sum = 1 开始
对于大于 1 的整数,1 一定是它的真因子。为了避免从 1 枚举到平方根时把自身也加进去,通常先把 sum 设为 1,然后从 2 开始枚举。
| num | 初始 sum | 原因 |
|---|---|---|
28 | 1 | 1 是真因子 |
6 | 1 | 1 是真因子 |
1 | 不适用 | 1 没有正真因子和为 1 |
易错点:
num <= 1要先返回 false,否则sum=1会把 1 误判成完美数。
三、因子为什么成对出现
如果 i 能整除 num,那么 num / i 也是因子。
28:
2 * 14
4 * 7
所以枚举到平方根就够了。小因子在左,大因子在右,一次发现两个。
四、平方数因子不能加两次
如果 num = 36,当 i = 6 时,配对因子也是 6。这时只能加一次:
if (i != num / i) sum += num / i;
否则平方根会被重复计算,导致因子和偏大。
五、循环条件的溢出问题
常见写法是 i * i <= num。在本题范围较小时没问题;如果输入可能很大,可以写成:
for (int i = 2; i <= num / i; i++)
这样避免 i * i 溢出。
六、复杂度与提前退出
枚举到平方根,时间复杂度 O(sqrt(n)),空间 O(1)。可以在 sum > num 时提前返回 false,因为正因子和只会继续增加。
如果 sum 已经超过 num,再加因子不会变小
提前退出不是必须,但能减少部分输入的运行时间。
七、常见误区与追问
- 误区:把 num 自身也加入因子和。 完美数看真因子,不包含自身。
- 误区:把 1 判成完美数。
num <= 1应直接 false。 - 误区:平方根因子加两次。 当
i == num / i时只能加一次。 - 追问:为什么只枚举到平方根? 因子成对出现,一个小于等于平方根,另一个大于等于平方根。
- 追问:复杂度是多少? 时间
O(sqrt(n)),空间O(1)。 - 追问:能不能用欧几里得-欧拉定理? 可以生成偶完美数,但判断题用因子枚举更直接。
八、加强记忆
完美数判断模板是:num <= 1 false,sum = 1,从 2 枚举到平方根,发现因子就加一对,平方根只加一次。它考的是“真因子”和“因子成对”,别把自身混进去。