Algorithm 高效地计算Pi十进制展开中要重复的前20位子字符串 问题

Algorithm 高效地计算Pi十进制展开中要重复的前20位子字符串 问题,algorithm,Algorithm,Pi=3.1415926535897932384626433。。。所以要重复的第一个2位数子串是26 找到要重复的前20位子字符串的有效方法是什么 约束条件 我有大约500 GB的Pi数字(每个数字1字节),还有大约500 GB的可用磁盘空间 我有大约5千兆字节的可用内存 我感兴趣的是一种有效的算法,它可以处理任意序列,而不是Pi本身的具体答案。换句话说,我对“print 123…456”格式的解决方案不感兴趣,即使它打印的数字是正确的 我试过的 我将每个子字符串放入一个哈希表并报告第一次冲

Pi=3.1415926535897932384626433。。。所以要重复的第一个2位数子串是26

找到要重复的前20位子字符串的有效方法是什么

约束条件
  • 我有大约500 GB的Pi数字(每个数字1字节),还有大约500 GB的可用磁盘空间

  • 我有大约5千兆字节的可用内存

  • 我感兴趣的是一种有效的算法,它可以处理任意序列,而不是Pi本身的具体答案。换句话说,我对“print 123…456”格式的解决方案不感兴趣,即使它打印的数字是正确的

我试过的 我将每个子字符串放入一个哈希表并报告第一次冲突

(哈希表被构造为排序链表的数组。数组中的索引由字符串的底部数字(转换为整数)给出,每个节点中存储的值是Pi展开中第一次出现子字符串的位置。)

在我用完RAM之前,这一切都很顺利

为了扩展到更长的序列,我考虑了:

  • 为特定范围内的所有子字符串生成哈希,然后继续搜索剩余的数字。这需要为每个范围重新扫描Pi的整个序列,因此变成N^2阶

  • Bucket将一组20位的子字符串排序为多个文件,然后使用哈希表分别查找每个文件中的第一个重复项。不幸的是,使用此方法时,磁盘空间不足,因此需要20次数据传递。(如果我以1000位数字开始,那么我将以1000个20位的子字符串结束。)

  • 每字节存储2位Pi以释放更多内存

  • 将基于磁盘的备份存储添加到我的哈希表中。我担心这会表现得非常糟糕,因为没有明显的参考位置

有更好的方法吗

更新
  • 我尝试了Adrian McCarthy的qsort方法,但这似乎比哈希查找重复项要慢一点

  • 我看了btilly关于并行算法的MapReduce建议,但它在我的单台计算机上受到严重的IO限制,因此不适合我(使用我的单磁盘驱动器)

  • 我实现了supercat的方法,花了昨晚的时间分割文件并搜索前180亿位中的19位子字符串

  • 这发现了16个匹配项,因此我使用Jarred的建议重新检查19位匹配项,以找到前20位匹配项

  • 搜索180亿位数需要花费大量时间 3小时分割文件,40分钟重新扫描文件以查找匹配项

    答复 20位子字符串84756845106452435773位于Pi的十进制扩展中的15494062637和17601613330位置


    谢谢大家

    也许类似的方法会奏效:

  • 搜索长度为2的重复子串(或一些小的基本情况),记录起始标记S={S_i}

  • 对于n=3..n,从S中的索引中查找长度为n的子字符串

  • 每次迭代,使用长度为n的子字符串更新S

  • 当n=20时,前两个标记将是您的答案

  • 您可能需要调整初始大小和步长(可能不需要每次按1进行调整)

    Trie

    RBarryYoung指出,这将超过内存限制

    数据结构可能是合适的。在一次过程中,您可以使用所看到的长度为n(例如,n=20)的每个前缀构建一个trie。当您继续处理时,如果您曾经到达已经存在的级别n的节点,那么您刚刚发现了一个重复的子字符串

    后缀匹配

    另一种方法涉及将扩展处理为字符串。这种方法可以找到公共后缀,但您需要公共前缀,所以从反转字符串开始。创建一个指针数组,每个指针指向字符串中的下一个数字。然后使用字典排序对指针进行排序。在C中,这类似于:

    qsort(array, number_of_digits, sizeof(array[0]), strcmp);
    
    当qsort结束时,指针数组中类似的子字符串将相邻。因此,对于每个指针,可以将该字符串与下一个指针指向的字符串进行有限的字符串比较。同样,在C中:

    for (int i = 1; i < number_of_digits; ++i) {
      if (strncmp(array[i - 1], array[i], 20) == 0) {
        // found two substrings that match for at least 20 digits
        // the pointers point to the last digits in the common substrings
      }
    }
    
    for(int i=1;i
    排序(通常)是O(n log_2n),之后的搜索是O(n)


    这种方法的灵感来源于。

    您的数据集相当大,因此需要某种“分而治之”的方法。我建议作为第一步,将问题细分为若干部分(例如100)。首先查看文件是否有任何重复的以00开头的20位序列,然后查看是否有任何以01开头的序列,等等,直到99为止。通过将所有以正确数字开头的20位序列写入文件,开始这些“主要过程”。如果前两位是常量,则只需写出最后18位;由于一个18位的十进制数字将适合8字节的“long”,因此输出文件可能包含大约5000000000个数字,占用40GB的磁盘空间。请注意,一次生成多个输出文件可能是值得的,这样可以避免必须读取源文件的每个字节100次,但如果只读取一个文件并写入一个文件,磁盘性能可能会更好

    生成特定“主通道”的数据文件后,必须确定其中是否存在任何重复项。细分