1. 引言
完美数是一种特殊的正整数,它等于其所有真因子(不包括自身)之和。这类数字在整数领域极为罕见——人类历史上仅发现少量完美数,前四个分别是6、28、496和8128。本文将深入探讨完美数的概念,并介绍三种不同的判断方法。
2. 理解完美数
通过具体例子理解完美数:
- 数字6的真因子为1、2、3,其和1+2+3=6
- 数字28的真因子为1、2、4、7、14,其和1+2+4+7+14=28
✅ 当真因子之和等于自身时,该数即为完美数。下面介绍三种判断方法。
3. 暴力法
最直接的方法是遍历所有可能的真因子并求和:
public static boolean isPerfectBruteForce(int number) {
int sum = 0;
for (int i = 1; i <= number / 2; i++) {
if (number % i == 0) {
sum += i;
}
}
return sum == number;
}
⚠️ 关键点:
- 遍历范围到
number/2
即可,因为最大真因子不超过number/2
- 时间复杂度为O(n),适合小数字但效率较低
4. 基于流的方法
利用Java Stream优化计算:
public static boolean isPerfectStream(int number) {
int sum = IntStream.rangeClosed(2, (int) Math.sqrt(number))
.filter(test -> number % test == 0)
.reduce(1, (s, test) -> s + test + (number / test));
return sum == number;
}
✅ 优化原理:
- 因子成对出现(如a和number/a),只需遍历到√number
- 同时累加因子及其配对值(如test和number/test)
- 时间复杂度降至O(√n),显著提升效率
5. 欧拉-欧几里得定理
基于数学定理的高效方法:
若$2^{p-1} \times (2^p - 1)$是素数(其中p为素数),则$(2^p - 1) \times 2^{p-1}$是完美数
基础实现:
public static boolean isPerfectEuclidEuler(int number) {
int p = 2;
int perfectNumber = (int) (Math.pow(2, p - 1) * (Math.pow(2, p) - 1));
while (perfectNumber <= number) {
if (perfectNumber == number) {
return true;
}
p++;
perfectNumber = (int) (Math.pow(2, p - 1) * (Math.pow(2, p) - 1));
}
return false;
}
❌ 性能陷阱:Math.pow()
计算效率低下,需优化为位运算:
public static boolean isPerfectEuclidEulerUsingShift(int number) {
int p = 2;
int perfectNumber = (2 << (p - 1)) * ((2 << p) - 1);
while (perfectNumber <= number) {
if (perfectNumber == number) {
return true;
}
p++;
perfectNumber = (2 << (p - 1)) * ((2 << p) - 1);
}
return false;
}
✅ 优化效果:
- 用
(2 << p)
替代Math.pow(2, p)
- 时间复杂度降至O(log n),适合大数计算
6. 分析与比较
三种方法性能对比(JMH基准测试,测试数33550336):
方法 | 吞吐量(ops/s) | 误差 | 特点 |
---|---|---|---|
暴力法 | 55.070 | ±2.674 | 简单但低效 |
流方法 | 96,114.246 | ±3,666.451 | 平衡简洁与效率 |
欧拉-欧几里得(优化版) | 99,191,865.954 | ±5,924,410.475 | 极致性能 |
📊 结论:
- 暴力法:仅适合小数字,大数时性能急剧下降
- 流方法:代码简洁,中等规模数据表现良好
- 欧拉-欧几里得(优化版):性能碾压其他方法,尤其适合大数场景
7. 总结
完美数判断的三种方法各有适用场景:
- 小规模数据:暴力法足够
- 中等规模:流方法平衡可读性与效率
- 大规模数据:优化后的欧拉-欧几里得定理是最佳选择
实际开发中,建议根据数据规模选择合适算法。源码可在GitHub获取。