使用C ++函数查找完美数字

时间:2018-02-17 06:51:46

标签: c++ function perfect-numbers

我无法接近程序中的特定问题。

首先,我需要确定一个数字是否完美。使用一个名为: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个完美数字。我可以采取哪些措施来减少测试如此大数字所需的时间?就像我可以通过规则跳过数字来测试我知道我不需要?非常感谢任何帮助。

1 个答案:

答案 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

所有前五个完美数字的计算时间少于二十分之一秒,仅占系统时间的八分之一秒。

该算法在世界上发挥了重要作用。