0

小质数 - 最多约 1,000,000,000,000 - 很容易从各种来源获得。Prime Pages (utm.edu)有前 5000 万个素数的列表,primos.mat.br上升到 10^12,像primesieve.org上提供的程序甚至更高。

然而,当涉及到接近 2^64 的数字时,只有 Primes.utm.edu 页面上提到的十个素数小于 2的幂,似乎就是这样。

数测试发现那里拒绝处理不适合双倍的数字,其他地方 - 其他地方 - 未能拒绝并且只是打印垃圾。primesieve.org 程序拒绝使用低于 2^64 至少 400 亿的数字,这并不能完全激发人们对其编码质量的信心。到处都是相同的结果:nada、zilch、niente。

筛子的齿轮和齿轮开始在 2^62 标记附近吱吱作响,接近 2^64 时几乎没有一个齿轮不发出响亮的吱吱声,威胁要破裂。因此,由于缺乏可靠的参考数据,在验证最困难的地方,测试实现的需求最大。primesieve.org 程序似乎是唯一一个至少可以工作到 2^63 左右的程序,但由于上述问题,我不太相信它。

那么如何才能验证接近 2^64 的筛子的正确操作呢?是否有可靠的列表包含一百万(或一千万或一亿)质数,刚好低于和高于 2 的幂,如 2^64、2^63 等等?或者是否有可靠的(值得信赖的、经过验证的、大量使用的)程序产生这样的序列或可以验证素数或素数列表?

一旦筛子得到验证,它就可以用来生成方便的列表,其中包含有大量有趣范围的总和/校验和,但如果没有这样的列表,情况似乎很困难......

PS:我将primesieve.org turbo siever的上限确定为0xFFFFFFF600000009 UINT64_MAX - 10 * UINT32_MAX。这意味着到目前为止,只有 10 * UINT32_MAX 最高质数根本没有任何参考数据......

4

3 回答 3

2

您可以将筛子的输出与不同的筛子进行比较,而不是寻找预先计算的列表。由 Tomás Oliveira e Silva 撰写的好筛子可在http://sweet.ua.pt/tos/software/prime_sieve.html

测试代码的另一种方法是测试筛子报告为素数的所有数字的素数(或者相反,测试筛子未报告为素数的所有数字的非素数)。一个很好的方法是 Baillie-Wagstaff 测试。您可以在 Thomas R. Nicely 找到一个高质量的实现http://www.trnicely.net/misc/bpsw.html

您可能还对位于 http://www.janfeitsma.nl/math/psp2/index 的 Jan Feitsma 的伪素数表感兴趣,该表已完成 2 64

于 2014-11-06T19:37:53.033 回答
1

首先,感谢您分享您的程序并致力于正确性。我认为进行测试很重要,并且在大小边界附近进行筛选是我花时间编写代码的事情。

“到处都是同样的结果:nada、zilch、niente。” 你看的不够仔细。有很多工具可以做到这一点。太糟糕了,primesieve 没有一直达到 2^64-1,但这并不意味着没有其他方法。

“那么,如何才能验证接近 2^64 的筛子的正确操作呢?” 我做的一件事是进行边缘案例测试,该测试贯穿 2^64-1 附近的所有起点/终点组合,验证许多方法都产生相同的结果。这依赖于有一个这些素数的列表来开始,但是有很多方法可以得到这些。这不仅在这个范围内测试筛子,而且测试开始/结束条件以确保那里没有问题。

生成低于 2^64 的一百万个素数的一些方法:

time perl -Mntheory=:all -E 'forprimes { say } ~0-44347170,~0' | md5sum

需要约 2 秒来生成 1M 个素数。我们可以强制使用不同的代码(Perl 或 GMP),使用素数测试等。有很多方法可以做到这一点,is_provable_prime($n)例如,包括循环和调用。还有其他 Perl 模块,包括 Math::Primality,尽管它们要慢得多。

echo 'forprime(i=2^64-44347170,2^64-1,print(i))' | time gp -f -q | md5sum

需要大约 13 秒来生成 1M 个素数。与 Perl 模块一样,有许多替代方法,包括循环调用 isprime,这是一个确定性例程(假设 Pari/GP 的非古代版本)。

#include <stdio.h>
#include <gmp.h>
int main(void) {
  mpz_t n;
  mpz_init_set_str(n,"18446744073665204445",10);
  mpz_nextprime(n, n);
  while (mpz_sizeinbase(n,2) < 65) {
    /* If you don't trust mpz_nextprime, one could add this:
     * if (!mpz_probab_prime_p(n, 100))
     *   { fprintf(stderr, "Bad nextprime!\n"); return -1; }
     */
    gmp_printf("%Zd\n",n);
    mpz_nextprime(n, n);
  }
  mpz_clear(n);
  return 0;
}

大约需要 30 秒并获得相同的结果。这个更令人怀疑,因为我不像 BPSW 或其中一种证明方法那样信任它的 25 个预设随机基础 MR 测试,但在这种情况下并不重要,因为我们看到结果匹配。添加额外的 100 个测试在时间上非常昂贵,但极不可能产生错误结果(我怀疑我们有重叠的碱基,所以这也很浪费)。

from sympy import nextprime
n = 2**64-44347170;
n = nextprime(n)
while n < 2**64:
  print n
  n = nextprime(n)

使用 Python 的 SymPy。不幸的是,当给定 2^64-1 时,primerange 会使用疯狂的内存,因此无法使用。执行简单的 nextprime 方法并不理想——大约需要 5 分钟,但会产生相同的结果(当前 SymPy isprime 使用 46 个素数基数,这比 2^64 下的确定性结果所需的要多得多)。

还有其他工具,例如 FLINT、GAP 等。

我意识到,由于您使用的是 Windows,因此世界很不稳定,很多事情都无法正常工作。我已经在 Windows 上测试了 Perl 的 ntheory,并且在命令提示符下使用 Cygwin 和 Strawberry Perl 我得到了相同的结果。假设 GMP 正常工作,则 GMP 代码应该可以正常工作。

编辑添加:如果您的结果与其中一种比较方法不匹配,则两种(或两者)之一是错误的。可能是比较代码错了!如果您发现并报告错误,它可以帮助每个人。这不太可能,但可能它们都以同样的方式出错,这就是为什么我喜欢与尽可能多的其他来源进行比较。对我来说,这比选择一个“黄金”代码进行比较更强大。尤其是如果您使用可能尚未经过彻底测试的古怪平台。


对于 BPSW,有一些实现:

  • 帕里。AES Lucas,在 Pari 源代码中,所以不确定它的可移植性。
  • TR 很好。强大的 Lucas,独立代码。
  • 大卫克利弗。标准、强或特强卢卡斯。独立库,非常清晰,非常好用。
  • 我的非 GMP 代码,包括 x86_64 的 asm Montgomery 数学。当然比 bigint 代码快很多。
  • 我的 GMP 代码。标准、强、AES 或超强 Lucas。比其他 bigint 代码更快。还有其他 Frobenius 和其他复合性测试。可以独立制作。
  • 我有一个使用 LibTomMath 的版本,我希望能进入其中一个 Perl6 VM。仅当您想使用 LTM 时才有趣。

所有验证与 Feitsma 数据。我敢肯定还有更多的实现。FLINT 有一个非常快的变体,但它并不是真正的 BPSW(但它已针对 2^64 以下的数字进行了验证)。

于 2014-11-24T10:48:24.847 回答
0

一般来说,一个人必须使用比试除法更少的幼稚技术,或者非常耐心。 (gp/PARI 文档)

对于 64 位整数,试除法所需的时间甚至是一个简单的筛子的数百万倍,更不用说像 Kim Walisch 的程序 ( primesieve.org ) 这样快几个数量级的纯种程序了。

我要验证的参考筛(有一个独立的 .cpp @ pastebin)在筛分接近 2^64 时每秒发现大约一百万个素数,而我从 gmp 实现中提取的试验除法代码甚至需要 20 秒才能找到一个. 将试验划分限制为预先筛选的素数(存储为每个素数一个字节的增量以进行快速迭代)将其速度提高了一个数量级,但它仍然在我的笔记本电脑上每秒输出不到一个素数。

因此,即使我使用所有我可以使用的内核,包括 Kindle、手机和烤面包机,试用部门也只能提供同理数量的参考数据。

更复杂的测试,如Miller-Rabin或 user448810 链接的Baillie-PSW,比试验部门快几个数量级。对于高达 2^64 的数字,Baillie-PSW 已被证实是确定性的(没有低于该阈值的强伪素数)。如果前 12 个素数用作基数,或者 Jim Sinclar 发现的 7-基数集,米勒-拉宾可能会或可能不会达到 2 ^ 64 .

经过 Baillie-PSW 验证 - 并且启动速度更快 - 这似乎是一个不错的选择。不幸的是,它也比筛子复杂几个数量级,这使得找到可靠的实现变得更加重要,这些实现可以在没有大量旋转的情况下进行编译,或者 - 理想情况下 - 作为二进制文件可用。

Thomas Nicely 的Baillie-PSW 页面有使用gmp的源代码,而gp/PARI可以使用 gmp 或它自己的代码。后者也可以作为二进制文件使用,这是非常幸运的,因为在 Windows 下的 MinGW 等异国情调的、另类的平台上构建 gmp 代码并非易事,即使使用MPIR代替 gmp 也是如此。

这为我们提供了一些大量数据,但仍然远远不足以验证筛子,因为即使覆盖 primesieve.org 上限留下的空白区域(10 * 2^32 个数字),它也太慢了几个数量级。

这就是 Will Ness 的 bigint 想法的用武之地。使用来自多个独立来源的参考数据,可以验证多达 1,000,000,000,000 个筛子的操作。将索引变量从 32 位切换到 64 位消除了大多数可能导致代码在较高区域混乱的边界情况,只留下极少数地方甚至 uint64_t 接近其极限。通过对这些地方进行彻底检查并被来自 Baillie-PSW 事业的测试用例大量覆盖,我们可以相当高地相信筛代码是好的。在从 10^12 到其上限的范围内对 primesieve.org 添加大量验证,将筛实现视为值得信赖就足够了。

随着筛子的启动和运行,很容易用大量数据覆盖任意范围。或者使用摘要,作为一种罐装/压缩验证手段,可以满足任何大小和形状的需求。这是我在前面提到的演示 .cpp中使用的,尽管我的实际代码混合了用于一般工作的优化摘要实现和用于快速自检因子筛位图的 128 位特殊原始内存校验和。

概括

于 2014-11-11T09:01:46.790 回答