我正在闲暇时间玩Euler项目,而且我需要进行一些重构。我已经实施了Miller-Rabin以及一些筛子。我之前听说过,对于小数字来说,筛子实际上更快,就像几百万以下一样。有没有人有这方面的信息?谷歌不是很有帮助。
答案 0 :(得分:9)
是的,您可以找到大多数算法,您可以随时交换空间。换句话说,通过允许使用更多内存,速度大大提高 * a 。
我实际上并不知道米勒 - 拉宾算法,但是,除非它比单个左移/加法和记忆提取更简单,否则它将被吹出水面。计算筛选。
这里重要的是预先计算。在性能方面,预先计算这样的事情是一个好主意,因为在不久的将来,第一百万个素数将不太可能发生变化: - )
换句话说,用以下内容创建筛子:
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个条目(每半个学位一个)。
最好的解决方案是优雅的和 devious: - )
对于它的价值,以下C代码将为您生成这样一个表,所有素数低于四百万(其中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),你就会发现这足以让黄金数量超过一百万(前1,031,130个素数)。
现在有一些方法可以减少存储空间,例如只存储奇数并调整宏来处理它,或使用位掩码而不是无符号字符。如果内存可用,我更喜欢简单的算法。
答案 1 :(得分:6)
我建议采用分层方法。首先,确保没有小的素因子。通过前20或30个素数进行试验分割,但如果使用巧妙的方法,则可以减少使用gcds所需的分割数量。此步骤过滤掉约90%的复合材料。
接下来,测试该数字是否为基数2的强可能素数(米勒 - 拉宾测试)。此步骤删除了几乎所有剩余的复合材料,但是一些稀有复合材料可以通过。
最后的证明步骤取决于你想要的大小。如果您愿意在小范围内工作,请在2-pseudoprimes列表上进行二进制搜索,使其达到您允许的最大值。如果那是2 ^ 32,那么您的列表将只有10,403个成员,因此查找应该只需要14个查询。
如果你想要达到2 ^ 64,现在就足够了(感谢Jan Feitisma的工作)来检查这个数字是否是BPSW伪伪。 (您还可以下载所有例外的3 GB列表,删除试验部门将删除的那些,并编写基于磁盘的二进制搜索。)T. R. Nicely有一个很好的页面,解释了如何合理有效地实现这一点。
如果你需要更高,请实现上述方法并将其用作Pocklington式测试的子程序。这延伸了“小小”的定义;如果您想了解有关这些方法的更多信息,请询问。
答案 2 :(得分:2)
作为预计算概念的变体,您可以先低价检查候选编号p
是否可以被2,3,5,7或11整除。如果不是,则声明{{1如果2 p-1 = 1(mod p),则为prime。这将在某些时候失败,但它的工作量高达1亿,因为我测试了它(预计算)。
换句话说,基数2的所有小型费马伪素数都可以被3,5,7或11中的一个整除。
编辑:
正如@starblue正确指出的那样,以上是完全错误的。我的程序中有一个错误。我能做的最好的就是将上述内容修改为:
如果候选人p
可以被2,3,5,7或11整除,则声明它是复合的;
如果p
是{4181921,44469471,5256091,9006401,9863461}之一,则声明它是复合的;
否则,如果p
通过Miller-Rabin测试基础2和5,则声明它为素数;
否则宣布它是复合的。
我测试了小于10,000,000的整数。也许一对不同的基地会做得更好。
请接受我的错误道歉。
编辑2:
好吧,我所看到的信息似乎已经在Miller-Rabin algorithm的维基百科页面上,标题为"Deterministic variants of the test"的部分。
答案 3 :(得分:1)
唯一的方法是对自己进行基准测试。当你这样做时,把它写下来,然后在网上发布。