1

我正在用 C 语言处理Project Euler #14并找出了基本算法;然而,对于大数字,它的运行速度慢得令人难以忍受,例如想要的 2,000,000;我推测是因为它必须一遍又一遍地生成序列,即使应该有一种方法来存储已知序列(例如,一旦我们达到 16,我们从以前的经验中知道下一个数字是 8、4、2 ,然后 1)。

我不完全确定如何使用 C 的固定长度数组来做到这一点,但必须有一个好方法(我敢肯定,这非常有效)。提前致谢。

这是我目前拥有的,如果有帮助的话。

#include <stdio.h>
#define UPTO 2000000

int collatzlen(int n);

int main(){
    int i, l=-1, li=-1, c=0;
    for(i=1; i<=UPTO; i++){
        if( (c=collatzlen(i)) > l) l=c, li=i;
    }
    printf("Greatest length:\t\t%7d\nGreatest starting point:\t%7d\n", l, li);
    return 1;
}

/* n != 0 */
int collatzlen(int n){
    int len = 0;
    while(n>1) n = (n%2==0 ? n/2 : 3*n+1), len+=1;
    return len;
}
4

3 回答 3

3

您的原始程序在我的机器上需要 3.5 秒。对你来说慢得令人难以忍受吗?

我又脏又丑的版本需要 0.3 秒。它使用一个全局数组来存储已经计算的值。并在未来的计算中使用它们。

int collatzlen2(unsigned long n);
static unsigned long array[2000000 + 1];//to store those already calculated

int main()
{
    int i, l=-1, li=-1, c=0;
    int x;
    for(x = 0; x < 2000000 + 1; x++) {
        array[x] = -1;//use -1 to denote not-calculated yet
    }

    for(i=1; i<=UPTO; i++){
        if( (c=collatzlen2(i)) > l) l=c, li=i;
    }
    printf("Greatest length:\t\t%7d\nGreatest starting point:\t%7d\n", l, li);

    return 1;
}

int collatzlen2(unsigned long n){
    unsigned long len = 0;
    unsigned long m = n;
    while(n > 1){
        if(n > 2000000 || array[n] == -1){ // outside range or not-calculated yet
            n = (n%2 == 0 ? n/2 : 3*n+1);
            len+=1;
        }
        else{ // if already calculated, use the value
            len += array[n];
            n = 1; // to get out of the while-loop
        }
    }
    array[m] = len;
    return len;
}
于 2010-06-22T16:51:44.627 回答
1

鉴于这本质上是一个一次性程序(即,一旦你运行它并得到答案,你将不会支持它多年:),我建议有一个全局变量来保存序列的长度已经计算过:

int lengthfrom[UPTO] = {};

如果您的最大大小是几百万,那么我们说的是兆字节的内存,它应该很容易立即放入 RAM。

以上将在启动时将数组初始化为零。在您的程序中 - 对于每次迭代,检查数组是否包含零。如果是这样 - 你将不得不继续进行计算。如果不是 - 那么你知道继续进行更多的迭代,所以只需将它添加到你到目前为止已经完成的数字中,你就完成了。当然,然后将新结果存储在数组中。

不要试图为这种大小的数组使用局部变量:这将尝试在堆栈上分配它,这将不够大并且可能会崩溃。

另外 - 请记住,使用此序列,值会上升和下降,因此您需要在程序中处理它(可能通过使数组长于 UPTO 值,并使用 aassert()来防止索引大于大小的数组)。

于 2010-06-22T15:59:02.710 回答
1

如果我没记错的话,您的问题不是算法慢:您现在拥有的算法对于 PE 要求您执行的操作来说已经足够快了。问题是溢出:你有时最终将你的数字乘以 3 多次,以至于它最终会超过可以存储在有符号整数中的最大值。使用无符号整数,如果仍然不起作用(但我很确定它确实如此),请使用 64 位整数 ( long long)。

这应该运行得非常快,但如果你想做得更快,其他答案已经解决了这个问题。

于 2010-06-22T16:00:45.657 回答