Day14 2023/01/21
难度:简单
题目
完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数。
它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。
例如:28,它有约数1、2、4、7、14、28,除去它本身28外,其余5个数相加,1+2+4+7+14=28。
输入n,请输出n以内(含n)完全数的个数。
数据范围:
输入描述:
输入一个数字n
输出描述:
输出不超过n的完全数的个数
示例
输入:1000
输出:3
思路一
采用暴力解法,计算1~n范围内每个数的真因子(即除了自身以外的约数)之和,如果等于本身,即该数就是完全数,接着计数器加1,最后返回计数器结果即可。
思路二
采用欧拉公式计算。
欧拉公式说明:
-
如果是质数,且也是质数,那么便是一个完全数。
-
例如,是一个质数,也是质数,(,是完全数。
-
例如,是一个质数,也是质数,(,是完全数。
关键点
-
思路一中外层循环控制被除数,内层循环控制除数,并判断该除数是否为真因数。
-
虽然思路一中采用的是暴力解法,但是优化了内层循环的循环次数。当
j <= n/2时即可退出循环。 因为没有大于自身 1/2 的真因子(这是个数学问题) -
思路二中自定义的函数
is_prime()用来判断是否为质数,其中循环退出的条件i <= sqrt(p);(即小于等于自身的平方根) ,这里不懂的同学可以看一下这个:链接
算法实现
c++代码实现1-暴力解法
#include <iostream>
#include <cmath>
using namespace std;
bool is_prime(int p);
//方法一:暴力解法
int main() {
int n;
cin >> n; //输入n
int count = 0; // 计数器
for (int i = 2; i <= n; i++) { //1肯定不是完全数
int sum = 1; //累加器,1是任何数的约数
for(int j = 2; j <= n/2; j++) { //大于自身1/2的约数是不存在的
if(i % j == 0 && i != j) sum += j; //i为被除数,j为除数。真因子不包括自生
}
if(sum == i) count++; //如果是完全数就加1
}
cout << "完全数个数为:" << count;
return 0;
}
- 时间复杂度 ---双层嵌套循环,其中n为数值大小,实际循环次数要小于
- 空间复杂度 --- 没有额外的辅助空间
c++代码实现2-欧拉方程
#include <cmath>
#include <iostream>
using namespace std;
bool is_prime(int p);
// 方法二:欧拉公式
// 如果p是质数,且2^p-1也是质数,那么(2^p-1)X2^(p-1)便是一个完全数
int main() {
int n;
cin >> n;
int count = 0; //计数器
for (int p = 2; p <= n; p++) { //1肯定不是完全数
int t = pow(2, p) - 1;
if (is_prime(p) && is_prime(t)) { //满足欧拉方程的要求,计算完全数
int perfect_num = pow(2, p - 1) * t;
if (perfect_num <= n)
count++;
else
break; //当大于n时就跳出循环,大大降低了循环次数
}
}
cout << "完全数个数为:" << count;
}
//判断是否为质数
bool is_prime(int p) {
for (int i = 2; i <= sqrt(p); i++) //判断是否有除了1和自生的因数(约数)
if (p % i == 0)
return false;
return true;
}
- 时间复杂度 --- 其中n为数值大小,m为当前判断数的平方根大小,最终循环次数远小于 n*m,当然也远小于暴力解法
- 空间复杂度 --- 没有额外的辅助空间
总结
- 感觉本题更偏向于数学问题。