【发布时间】:2017-08-30 06:12:59
【问题描述】:
我有一个程序要求我找到直到10**10-1 (10,000,000,000) 的素数。我写了一个埃拉托色尼筛子来做这个,它工作得非常好(而且准确)高达10**9 (1,000,000,000)。我通过计算它找到的素数来确认它的准确性,它与the chart found here. 上的 50,847,534 的值匹配。我使用unsigned int 作为存储类型,它在大约 30 秒内成功找到了所有素数。
但是,10**10 要求我使用更大的存储类型:long long int。一旦我切换到这个,程序的运行速度就会明显变慢(它已经超过 3 小时而且它仍在工作)。以下是相关代码:
typedef unsigned long long ul_long;
typedef unsigned int u_int;
ul_long max = 10000000000;
u_int blocks = 1250000000;
char memField[1250000000];
char mapBit(char place) { //convert 0->0x80, 1->0x40, 2->0x20, and so on
return 0x80 >> (place);
}
for (u_int i = 2; i*i < max; i++) {
if (memField[i / 8] & activeBit) { //Use correct memory block
for (ul_long n = 2 * i; n < max; n += i) {
char secondaryBit = mapBit(n % 8); //Determine bit position of n
u_int activeByte = n / 8; //Determine correct memory block
if (n < 8) { //Manual override memory block and bit for first block
secondaryBit = mapBit(n);
activeByte = 0;
}
memField[activeByte] &= ~(secondaryBit); //Set the flag to false
}
}
activeBit = activeBit >> 1; //Check the next
if (activeBit == 0x00) activeBit = 0x80;
}
我认为由于10**10 比10**9 大 10 倍,所以它应该花费 10 倍的时间。这其中的缺陷在哪里?为什么更改为 long long 会导致如此严重的性能问题,我该如何解决?我认识到数字变大了,所以应该会慢一些,但只是到最后。有什么我想念的吗。
注意:我意识到 long int should technically be large enough 但我的 limits.h 说即使我正在编译 64 位也不是。这就是为什么我使用long long int 以防有人想知道。另外,请记住,我没有受过计算机科学培训,只是一个业余爱好者。
编辑:只是在 x86-64 的“发布”中运行它,并建议了一些调试语句。我得到以下输出:
看起来我击中了 u_int 绑定。我不知道为什么i 变得那么大。
【问题讨论】:
-
您的函数更有可能是 O(n*n) 或更糟。所以如果你从 10 到 9 的幂到 10 的 10 的幂,它会影响持续时间 10 的 (2*10)/10 的 (9*2) 的幂,即 100,而不是10. 或者更糟。
-
使用
<stdint.h>来避免依赖于平台的整数大小的怪异。 -
你当然可以做一些优化。只为奇数做一个筛子。并以 n = i*i 开始内部循环,因为 n 的所有较小倍数都已被删除。确保 mapBit 是内联的。并编译为 64 位系统,而不是 32 位。这将产生巨大的影响。
-
也许我错过了一些东西,但 OP 似乎并没有直接比较两个代码在相同问题大小上的性能。现在所有 OP 可以说的是
long long int版本需要更长的时间来解决一个更大的问题,而不是它比其他版本运行得更慢。 -
@AjayBrahmakshatriya:这个筛子绝对不是 O (n^2)。我认为它是 O (n log log n),实际上是线性的。
标签: c performance sieve-of-eratosthenes sieve