在c中存储已知序列

时间:2010-06-22 15:48:54

标签: c collatz

我正在使用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;
}

3 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:1)

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

int lengthfrom[UPTO] = {};

如果你的最大尺寸是几百万,那么我们说的是兆字节的内存,它应该可以很容易地同时放入RAM中。

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

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

另外 - 请记住,使用此序列,值会同时上升,因此您需要处理程序中的值(可能是因为数组的长度超过UPTO值,并使用assert()防止索引大于数组的大小。)

答案 2 :(得分:1)

如果我没记错的话,你的问题并不是一个缓慢的算法:你现在拥有的算法足够快,因为PE要求你这么做。问题是溢出:你有时会将你的数字乘以3这么多次,最终会超过可以存储在signed int中的最大值。使用无符号整数,如果仍然不起作用(但我确信它确实如此),请使用64位整数(long long)。

这应该非常快,但如果你想更快地完成,其他答案已经解决了。