扯一会儿
哈哈哈哈!我终于更了
事情是这样的——
春风得意马蹄疾,一日看尽长安花。这几天没有文案可写,又开学了,在书上面看见一个有趣的东西:
完全数!
没错,做着数学题脑子又飞到编程上去了。。。
开始搞!
开始搞
什么是完全数???
完全数就是它的所有因数(也称约数)除了它本身的和正好等于这个数,所有因数除了它本身也是连续的数。
比如:
6就是一个完全数。它的约数有1、2、3、6。去掉它的本身,剩下1、2、3,正好1 + 2 + 3 = 6,1、2、3也是连续的。
题目要求
题目
请输出2 ~ 1000000000000000000(即10^18)的完全数,并统计数量。
样例输入
(无)
样例输出
6
28
...
一共有...个完全数。
代码思想:
方法1:因数累加
这个比较好想:
#include <iostream>
using namespace std;
int s;
const long long MAX = 1e18; // 设定一个合理的上限
int main(void)
{
for(int i = 2; i <= MAX; i++)
{
int sum = 0; // 在每次外层循环开始时重置sum
// 找出所有真因数并累加
for(int j = 1; j < i; j++) // 注意这里是j < i而不是j <= i
{
if(i % j == 0)
sum += j;
}
// 判断是否为完全数
if(i == sum)
cout << i << endl;
}
cout << "一共有" << s << "个完全数";
return 0;
}
但。。。。。。
这可不是我想要的效果啊啊啊啊啊!w(゚Д゚)w
太慢了。。。。。。有没有更快的方法呢?
方法2:欧几里得 - 欧拉定理
#include <iostream>
#include <cmath>
using namespace std;
int s;
// 快速判断素数的函数(针对梅森数优化)
bool isPrime(long long num) {
if (num <= 1) return false;
if (num == 2) return true;
if (num % 2 == 0) return false;
// 对于梅森数,只需检查到sqrt(num)即可
for (long long i = 3; i <= sqrt(num); i += 2) {
if (num % i == 0) return false;
}
return true;
}
int main() {
const long long MAX = 1e18; // 设定一个合理的上限
// 已知的梅森素数指数p(截至2023年发现51个)
int knownPrimes[] = {2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127};
for (int p : knownPrimes) {
long long part1 = pow(2, p-1);
long long part2 = pow(2, p) - 1;
// 防止溢出
if (part1 > MAX / part2) break;
long long perfect = part1 * part2;
if (perfect <= MAX) {
s++;
cout << perfect << endl;
}
}
cout << "一共有" << s << "个完全数";
// 对于较小范围,也可以使用优化的暴力算法
// 但这里仅展示数学方法
return 0;
}
算法优化说明:
- 数学方法:利用欧几里得 - 欧拉定理,时间复杂度降为 O (k),其中 k 是已知梅森素数的数量。
- 素数判断优化:对于梅森数 2^p -1,使用试除法到平方根即可。
- 溢出处理:在计算前检查是否会溢出,确保程序安全运行。
注意事项:
- 该算法只能找到与已知梅森素数对应的完全数。目前已知的梅森素数有 51 个,但非常大(最大的有超过 2400 万位)。
- 对于普通计算机,输出超过前 12 个完全数(对应代码中的已知素数)会导致数值溢出或内存不足。
- 如果需要寻找特定范围内的完全数,可以结合使用优化的暴力算法(例如只检查偶数并优化因数查找)。