【发布时间】:2014-10-28 10:47:10
【问题描述】:
是否有任何聪明的算法可以计算数百万或数十亿素数的高质量校验和? IE。具有最大的错误检测能力并且可能是可分段的?
动机:
小素数 - 大小高达 64 位 - 可以根据需要以每秒数百万的速度进行筛分,方法是使用一个小位图来筛选潜在因子(最多 2^32-1)和第二个位图来筛选目标范围内的数字。
算法和实现相当简单明了,但魔鬼在细节中:值往往会在任何地方推动 - 或超过 - 内置整数类型的限制,边界情况比比皆是(可以这么说),甚至浮点数的差异如果编程没有适当的防御性,严格性可能会导致破坏。更不用说优化编译器可能造成的混乱,即使是在静态库中已经编译、已经测试的代码上(如果使用链接时代码生成)。更不用说更快的算法往往更复杂,因此更脆弱。
这有两个后果:除非使用最终的可执行映像执行测试,否则测试结果基本上没有意义,并且非常需要在运行时验证正常使用期间的正确操作。
对照预先计算的值进行检查可以提供最高程度的置信度,但所需的文件又大又笨重。具有 1000 万个素数的文本文件有大约 100 MB 未压缩和超过 10 MB 压缩;存储字节编码的差异需要每个素数一个字节,熵编码最多可以将大小减少一半(1000 万个素数需要 5 MB)。因此,即使是一个仅涵盖高达 2^32 的小因素的文件,其重量也会达到 100 MB 左右,并且解码器的复杂性将超过窗口筛本身的复杂性。
这意味着检查文件是不可行的,除非作为对新建可执行文件的最终版本检查。更不用说值得信赖的文件来之不易。 Prime Pages 提供前 5000 万个素数的文件,即使是惊人的 primos.mat.br 也只能达到 1,000,000,000,000。这是不幸的,因为许多边界情况(== 需要测试)发生在 2^62 和 2^64-1 之间。
这会留下校验和。这样,空间需求将是微不足道的,并且仅与测试用例的数量成正比。我不想要求提供像 MD5 或 SHA-256 这样的体面校验和,并且目标数字都是素数,应该可以通过对数字进行一些简单的操作来生成高质量、高分辨率的校验和自己。
这是我到目前为止所想出的。原始摘要由四个 64 位数字组成;最后,它可以折叠到所需的尺寸。
for (unsigned i = 0; i < ELEMENTS(primes); ++i)
{
digest[0] *= primes[i]; // running product (must be initialised to 1)
digest[1] += digest[0]; // sum of sequence of running products
digest[2] += primes[i]; // running sum
digest[3] += digest[2] * primes[i]; // Hornerish sum
}
每个素数有两个(非相关)muls,速度足够好,除了简单的总和之外,每个组件总是发现所有错误,我试图偷偷溜过摘要。但是,我不是数学家,经验测试并不能保证有效性。
是否有一些数学属性可以用来设计——而不是像我那样“煮”——一个合理、可靠的校验和?
是否可以设计校验和以使其可步进,在某种意义上,子范围可以单独处理,然后将结果与一些算术相结合以给出相同的结果,就好像整个范围已被校验和一样一气呵成?与当今所有高级 CRC 实现趋于具有相同的功能,以启用并行处理。
编辑当前方案的基本原理是:计数、总和和乘积不依赖于将素数添加到摘要中的顺序;它们可以在单独的块上计算,然后组合起来。校验和确实取决于顺序;这就是它存在的理由。但是,如果可以以某种方式组合两个连续块的两个校验和以给出组合块的校验和,那就太好了。
有时可以根据外部来源验证计数和总和,例如oeis.org 上的某些序列,或针对primos.mat.br 上的 1000 万个素数批次等来源(索引给出第一个和最后一个素数,数字 = = 1000 万是隐含的)。但是,产品和校验和没有这样的运气。
在我花费大量时间和计算能力来计算和验证摘要之前,涵盖了高达 2^64 的整个小因子范围,我想听听专家对此的看法......
我目前在 32 位和 64 位变体中测试的方案如下所示:
template<typename word_t>
struct digest_t
{
word_t count;
word_t sum;
word_t product;
word_t checksum;
// ...
void add_prime (word_t n)
{
count += 1;
sum += n;
product *= n;
checksum += n * sum + product;
}
};
这样做的好处是 32 位摘要分量等于相应 64 位值的下半部分,这意味着即使需要快速 32 位验证,也只需要计算存储 64 位摘要。一个 32 位版本的摘要可以在这个简单的sieve test program@pastebin 中找到,用于动手实验。可以在 a sieve that works up to 2^64-1 的更新粘贴中找到修订后的模板版本中的完整 Monty。
【问题讨论】:
-
前 23,163,298 个素数被认为是压缩友好的。它是每个间隙
-
@vitaly-t:通过注意除了 2 和 3 之间的所有间隙都是偶数,压缩友好领域可以扩展很多。这使您可以覆盖所有小于 303,371,455,241(第一次出现的间隙 > 512)的素数,远远超出 32 位范围。
-
这是一个很好的收获!我现在可能会修改my implementation for this。
-
它带来了两个问题:1)您需要将段映射的大小加倍(一个用于按索引快速定位),因为 32 位不再足够 2)额外的位在按索引访问期间进行操作,这需要尽可能快。所以可以进一步压缩,只是不再那么压缩友好了:)
-
@vitaly-t:至少这允许它为 all 32 位素数使用字节间隙,这对于某些应用程序来说是一个巨大的胜利。唯一真正复杂的是对素数 2 的特殊处理;间隙值的缩放要么是完全免费的(当使用索引缩放时),要么非常便宜以至于几乎没有几率(移位指令)。
标签: primes checksum discrete-mathematics