小数字的最快素数测试

时间:2010-09-20 21:47:53

标签: math primes sieve

我正在闲暇时间玩Euler项目,而且我需要进行一些重构。我已经实施了Miller-Rabin以及一些筛子。我之前听说过,对于小数字来说,筛子实际上更快,就像几百万以下一样。有没有人有这方面的信息?谷歌不是很有帮助。

4 个答案:

答案 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)

唯一的方法是对自己进行基准测试。当你这样做时,把它写下来,然后在网上发布。