我正在使用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;
}
答案 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
)。
这应该非常快,但如果你想更快地完成,其他答案已经解决了。