【问题标题】:storing known sequences in c在c中存储已知序列
【发布时间】:2010-06-22 15:48:54
【问题描述】:

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

【问题讨论】:

标签: c collatz


【解决方案1】:

您的原始程序在我的机器上需要 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 只是存储一堆,如果 3n+1 大于您存储的最大值,请重新计算。 2~3 行非常简单的代码,速度提高了一个数量级!
【解决方案2】:

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

int lengthfrom[UPTO] = {};

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

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

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

另外 - 请记住,使用这个序列,值会上升和下降,因此您需要在程序中处理这个问题(可能通过使数组长于 UPTO 值,并使用assert() 来保护针对大于数组大小的索引)。

【讨论】:

  • 这种方法的问题是你无法知道正确的 UPTO 值。
  • 是的。但是考虑到目标只是找到特定问题的正确答案(而不是生成一个在所有输入上都成功的程序),增加数组的大小并再次运行它并没有什么坏处。如果这不起作用(一旦数组开始接近机器内存的大小),那么可以实现更复杂的方法之一(例如带有桶的哈希表) - 但您可以通过尝试简单的方法来节省时间方式第一:)
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 2017-09-02
    • 2020-03-19
    • 1970-01-01
    • 2020-04-23
    相关资源
    最近更新 更多