我无法接近程序中的特定问题。
首先,我需要确定一个数字是否完美。使用一个名为:bool isPerfect(int n)的函数,我在这里做了:
bool isPerfect(int n) {
sum = 0;
for (int i = 1; i < n; i++) {
if (n % i == 0)
sum += i;
}
if (sum == n) {
return true;
}
else {
return false;
}
}
我面临的问题是第二步。我需要创建生成许多整数进行测试的代码。然后测试这些整数,直到我找到并打印出五个完美的整数。我写的意大利面条代码最终需要花费太长时间来计算第5个完美数字。我可以采取哪些措施来减少测试如此大数字所需的时间?就像我可以通过规则跳过数字来测试我知道我不需要?非常感谢任何帮助。
答案 0 :(得分:1)
Euclid证明 2 p-1 (2 p - 1)是2p − 1
为素数时的偶数完全数。请参阅:Wikipedia - Even perfect numbers这提供了一个框架,用于在几乎简单的小型计算集中生成完美数字的所有候选项。这里的目标是找到前五个完美数字。幸运的是,前五个很容易适合4字节unsigned
数据类型,并且可以在比输入[Ctrl + C]所用的时间更短的时间内计算。
要解决问题,首先要根据上面的公式计算完美数字的候选者。你可以使用pow
提供的math.h
函数,即使它是专为浮点使用而设计的,或者你可以简单地创建自己的函数来循环候选p
这个数字的值时间要求将p
自身乘以最终结果的数组,例如如下所示:
unsigned upow (unsigned v, unsigned n) /* unsigned pow() */
{
unsigned tmp = 1;
if (n == 0)
return 1;
while (n--)
tmp *= v;
return tmp;
}
(注意:溢出检查应该被添加以防止无符号溢出 - 使用unsigned
4字节类型的完美数字超过5 th < / SUP>)
算法的其余部分相当简单,您只需将候选者中的数字从1
循环到candidate / 2
(包括),以确保找到所有因子,求和并存储在数组中持有个别除数以供日后显示。
该方法的一个简短例子由:
unsigned sum = 0, i, j, p, pn, pncount = 0; /* variable declarations */
for (p = 2; p < 32; p++) { /* generate candidate from */
unsigned divisors[NELEM] = {0}, n = 0; /* divisors array and ndx */
pn = upow (2, p - 1) * (upow (2, p) - 1); /* 2^(n - 1) * (2^n - 1) */
for (i = 1; i <= pn / 2; i++) { /* find divisors & sum */
if (pn % i == 0) {
sum += i;
divisors[n++] = i; /* store divisor to array */
}
if (n == NELEM) { /* protect array bound */
fprintf (stderr, "error: f full.\n");
return 1;
}
}
if (sum == pn) { /* test whether candidate is Perfect Number */
printf ("Perfect number: %10u :", pn);
for (j = 0; j < n; j++) /* output divisors */
printf (j ? ", %u" : " %u", divisors[j]);
putchar ('\n');
if (++pncount == MAXPN) /* check against limit */
break;
}
sum = 0; /* reset sum for next iterations */
}
剩下的就是添加stdio.h
标题,为我们生成的最大完整数字和divisiors数组声明一些常量。完全放在一起,你可以做类似以下的事情:
#include <stdio.h>
#define MAXPN 5 /* const - max perfect numbers to find */
#define NELEM 4096 /* const - elements in divisors array */
unsigned upow (unsigned v, unsigned n) /* unsigned pow() */
{
unsigned tmp = 1;
if (n == 0)
return 1;
while (n--)
tmp *= v;
return tmp;
}
int main (void) {
unsigned sum = 0, i, j, p, pn, pncount = 0; /* variable declarations */
for (p = 2; p < 32; p++) { /* generate candidate from */
unsigned divisors[NELEM] = {0}, n = 0; /* divisors array and ndx */
pn = upow (2, p - 1) * (upow (2, p) - 1); /* 2^(n - 1) * (2^n - 1) */
for (i = 1; i <= pn / 2; i++) { /* find divisors & sum */
if (pn % i == 0) {
sum += i;
divisors[n++] = i; /* store divisor to array */
}
if (n == NELEM) { /* protect array bound */
fprintf (stderr, "error: f full.\n");
return 1;
}
}
if (sum == pn) { /* test whether candidate is Perfect Number */
printf ("Perfect number: %10u :", pn);
for (j = 0; j < n; j++) /* output divisors */
printf (j ? ", %u" : " %u", divisors[j]);
putchar ('\n');
if (++pncount == MAXPN) /* check against limit */
break;
}
sum = 0; /* reset sum for next iterations */
}
}
表现是关键。正如您所发现的那样,由于蛮力方法需要数万亿次计算迭代可能的组合和可能的完美数字,即使在快速机器上,您可能有时间进行短暂休假,然后才能完成5 案件。
测试输出显示前五个完整数字的算法是可靠的,例如
*示例使用/输出**
$ ./bin/perfnumber
Perfect number: 6 : 1, 2, 3
Perfect number: 28 : 1, 2, 4, 7, 14
Perfect number: 496 : 1, 2, 4, 8, 16, 31, 62, 124, 248
Perfect number: 8128 : 1, 2, 4, 8, 16, 32, 64, 127, 254, 508,
1016, 2032, 4064
Perfect number: 33550336 : 1, 2, 4, 8, 16, 32, 64, 128, 256, 512,
1024, 2048, 4096, 8191, 16382, 32764, 65528,
131056, 262112, 524224, 1048448, 2096896,
4193792, 8387584, 16775168
(注意:组成sum
的独立除数的输出被整齐地包裹起来以避免在这里滚动SO,通常它们只是在完美数字后面按顺序给出) 。
至于代码的时间,只需要对time
进行一次简单的调用就可以对所需的计算时间进行相对比较,例如
近似运行时
$ time ./bin/perfnumber
<snip output>
real 0m0.146s
user 0m0.139s
sys 0m0.008s
所有前五个完美数字的计算时间少于二十分之一秒,仅占系统时间的八分之一秒。
该算法在世界上发挥了重要作用。