是的,您会发现大多数算法都可以用空间换取时间。换句话说,通过允许使用更多内存,速度大大提高*a。
我实际上并不知道 Miller-Rabin 算法,但除非它比单次左移/添加和内存提取更简单,否则它会被预计算筛。
这里重要的是预先计算好的。就性能而言,预先计算这样的事情是个好主意,因为前一百万个素数在不久的将来不太可能改变:-)
换句话说,使用以下内容创建您的筛子:
unsigned char primeTbl[] = {0,0,1,1,0,1,0,1,0,0,0,1};
#define isPrime(x) ((x < sizeof(primeTbl) ? primeTbl[x] : isPrimeFn(x))
关于不将a++ 之类的东西传递给宏的所有常见警告。这为您提供了两全其美的优势,对“小”素数的快速表查找,对超出范围的素数回退到计算方法。
显然,您会使用其他方法之一编写程序来生成该查找表 - 您真的不想手动输入所有内容。
但是,与所有优化问题一样,衡量,不要猜测!
*a 一个典型的例子是我曾经为嵌入式系统编写的一些三角函数。这是一份有竞争力的合同投标,而且系统的存储空间比 CPU 多一点。
我们实际上赢得了合同,因为我们的功能基准数据击败了竞争对手。
为什么?因为我们将这些值预先计算到了最初在另一台机器上计算的查找表中。通过明智地使用归约(将输入值降低到 90 度以下)和触发属性(余弦只是正弦的相移并且其他三个象限与第一个象限相关的事实),我们将查找表降低到180 个条目(每半度一个)。
最好的解决方案是那些优雅的和狡猾的:-)
不管怎样,下面的 C 代码将为您生成这样一个表,所有低于 400 万的素数(其中 283,000 个)。
#include <stdio.h>
static unsigned char primeTbl[4000000];
int main (void) {
int i, j;
for (i = 0; i < sizeof(primeTbl); i++)
primeTbl[i] = 1;
primeTbl[0] = 0;
primeTbl[1] = 0;
for (i = 2; i < sizeof(primeTbl); i++)
if (primeTbl[i])
for (j = i + i; j < sizeof(primeTbl); j += i)
primeTbl[j] = 0;
printf ("static unsigned char primeTbl[] = {");
for (i = 0; i < sizeof(primeTbl); i++) {
if ((i % 50) == 0) {
printf ("\n ");
}
printf ("%d,", primeTbl[i]);
}
printf ("\n};\n");
printf ("#define isPrime(x) "
"((x < sizeof(primeTbl) ? primeTbl[x] : isPrimeFn(x))\n");
return 0;
}
如果您可以将 primeTbl 表增加到 1600 万个条目 (16M),您会发现这足以将素数保持在 100 万以上(前 1,031,130 个素数)。
现在有一些方法可以减少存储空间,例如只存储奇数并调整宏来处理它,或者使用位掩码而不是无符号字符。如果内存可用,我自己更喜欢算法的简单性。