在c中存储已知序列

发布于 2024-09-07 06:57:22 字数 806 浏览 5 评论 0原文

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

I'm working on Project Euler #14 in C and have figured out the basic algorithm; however, it runs insufferably slow for large numbers, e.g. 2,000,000 as wanted; I presume because it has to generate the sequence over and over again, even though there should be a way to store known sequences (e.g., once we get to a 16, we know from previous experience that the next numbers are 8, 4, 2, then 1).

I'm not exactly sure how to do this with C's fixed-length array, but there must be a good way (that's amazingly efficient, I'm sure). Thanks in advance.

Here's what I currently have, if it helps.

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

玻璃人 2024-09-14 06:57:22

你原来的程序在我的机器上需要 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;
}

Your original program needs 3.5 seconds on my machine. Is it insufferably slow for you?

My dirty and ugly version needs 0.3 seconds. It uses a global array to store the values already calculated. And use them in future calculations.

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;
}
来世叙缘 2024-09-14 06:57:22

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

int lengthfrom[UPTO] = {};

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

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

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

另外 - 请记住,在这个序列中,值会上升和下降,因此您需要在程序中处理这个问题(可能是让数组比 UPTO 值长,并使用 assert() 以防止索引大于数组的大小)。

Given that this is essentially a throw-away program (i.e. once you've run it and got the answer, you're not going to be supporting it for years :), I would suggest having a global variable to hold the lengths of sequences already calculated:

int lengthfrom[UPTO] = {};

If your maximum size is a few million, then we're talking megabytes of memory, which should easily fit in RAM at once.

The above will initialise the array to zeros at startup. In your program - for each iteration, check whether the array contains zero. If it does - you'll have to keep going with the computation. If not - then you know that carrying on would go on for that many more iterations, so just add that to the number you've done so far and you're done. And then store the new result in the array, of course.

Don't be tempted to use a local variable for an array of this size: that will try to allocate it on the stack, which won't be big enough and will likely crash.

Also - remember that with this sequence the values go up as well as down, so you'll need to cope with that in your program (probably by having the array longer than UPTO values, and using an assert() to guard against indices greater than the size of the array).

甲如呢乙后呢 2024-09-14 06:57:22

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

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

If I recall correctly, your problem isn't a slow algorithm: the algorithm you have now is fast enough for what PE asks you to do. The problem is overflow: you sometimes end up multiplying your number by 3 so many times that it will eventually exceed the maximum value that can be stored in a signed int. Use unsigned ints, and if that still doesn't work (but I'm pretty sure it does), use 64 bit ints (long long).

This should run very fast, but if you want to do it even faster, the other answers already addressed that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文