适用于非常大的质数的素数硬盘存储 - 阿特金筛选

时间:2015-08-21 04:38:11

标签: python math primes sieve sieve-of-atkin

我已经实现了Sieve of Atkin,它的效果非常接近100,000,000左右。除此之外,它因内存问题而崩溃。

在算法中,我想用基于硬盘的阵列替换基于内存的阵列。 Python" wb"文件功能和Seek功能可以解决问题。在我发明新轮子之前,有人可以提供建议吗?一开始就出现两个问题:

  1. 有没有办法去" chunk" Atkin的Sieve在内存中处理段,
  2. 有没有办法暂停活动并稍后再回来 - 建议我可以序列化内存变量并恢复它们。
  3. 为什么我这样做?寻找娱乐和保持面条工作的老geezer。

4 个答案:

答案 0 :(得分:5)

在Python中实现SoA听起来很有趣,但请注意它在实践中可能比SoE慢。对于一些好的单片SoE实现,请参阅RWH's StackOverflow post。这些可以让您了解非常基本的实现的速度和内存使用情况。 numpy版本将在我的笔记本电脑上筛选超过10,000M。

你真正想要的是分段筛。这使您可以将内存使用限制到某个合理的限制(例如1M + O(sqrt(n)),如果需要可以减少后者)。在primesieve.org中显示了一个很好的C ++讨论和代码。您可以在Python中找到各种其他示例。 primegen,伯恩斯坦对SoA的实施,是作为分段筛子实现的(你的问题1:是的,SoA可以被分割)。这与筛选范围密切相关(但不完全相同)。这就是我们如何使用筛子在几分之一秒内找到10 ^ 18和10 ^ 18 + 1e6之间的质数 - 我们当然不会将所有数字筛选到10 ^ 18 + 1e6。

IMO涉及到硬盘驱动器的方向是错误的。我们应该能够比从驱动器中读取值更快地筛分(至少在良好的C实现中)。一个远程和/或分段筛子应该做你需要的。

有更好的存储方式,这将有所帮助。我的SoE和其他几个一样,使用mod-30轮,因此每30个整数有8个候选,因此每30个值使用一个字节。看起来伯恩斯坦的SoA做了类似的事情,每60个值使用2个字节。 RWH的python实现并不完全存在,但足够接近每30个值10位。不幸的是,看起来Python的本机bool数组每位使用大约10个字节,numpy是每位一个字节。您可以使用分段筛,也不要过于担心,或者在Python存储中找到更有效的方法。

答案 1 :(得分:3)

首先,您应该确保以有效的方式存储数据。您可以使用位图轻松存储12.5Mb内存中最多100,000,000个素数的数据,通过跳过明显的非素数(偶数等等),您可以使表示更加紧凑。这也有助于将数据存储在硬盘驱动器上。你在100,000,000个素数时陷入困境表明你没有有效地存储数据。

如果您没有得到更好的答案,可以提供一些提示。

  

1.有没有办法去" chunk" Atkin的Sieve在内存中处理段

是的,对于类似Eratosthenes的部分,您可以做的是在筛选列表中运行多个元素" parallell" (一次一个块),这样可以最大限度地减少磁盘访问。

第一部分有点棘手,你想要做的是以更加有序的顺序处理4*x**2+y**23*x**2+y**23*x**2-y**2。一种方法是首先计算它们然后对数字进行排序,有一些排序算法在驱动器存储上运行良好(仍为O(N log N)),但这会损害时间复杂度。一种更好的方法是迭代xy,使得您一次在一个块上运行,因为一个块由一个间隔确定,例如,您可以简单地迭代所有{ {1}}和x y

  

2.有没有办法暂停活动并稍后再回来 - 建议我可以序列化内存变量并恢复它们

为了实现这一点(无论程序如何以及何时终止),您必须首先记录磁盘访问(fx使用SQL数据库来保存数据,但小心您可以自己完成)。

第二,因为第一部分的操作不是无效的,所以你必须确保你不重复这些操作。但是,由于您将逐块运行该部分,因此您可以简单地检测哪个是最后一个块处理并在那里继续(如果您最终可以使用部分处理的块,则只需丢弃该块并重做该块)。对于Erastothenes部分它是无能为力的,所以你可以直接通过所有这些,但是为了提高速度,你可以在完成它们的筛分后存储生产的素数列表(所以你会在最后一次筛选后恢复产生了素数。

作为副产品,您甚至应该能够以这样的方式构建程序,即使在第二步运行时也可以保持第一步的数据,从而在稍后通过继续扩展限制第一步,然后再次运行第二步。甚至可能有两个程序,当你厌倦了它然后将它输出到Eratosthenes部分时,你终止第一个程序(因此不必定义限制)。

答案 2 :(得分:1)

您可以尝试使用signal处理程序来捕获应用程序何时终止。然后,这可以在终止之前保存当前状态。以下脚本显示重新启动时继续的简单数字计数。

import signal, os, cPickle

class MyState:
    def __init__(self):
        self.count = 1

def stop_handler(signum, frame):
    global running
    running = False

signal.signal(signal.SIGINT, stop_handler)
running = True
state_filename = "state.txt"

if os.path.isfile(state_filename):
    with open(state_filename, "rb") as f_state:
        my_state = cPickle.load(f_state)
else:
    my_state = MyState()

while running:
    print my_state.count
    my_state.count += 1

with open(state_filename, "wb") as f_state:
    cPickle.dump(my_state, f_state)

至于改进磁盘写入,您可以尝试使用1Mb或更大的缓冲区来增加Python自己的文件缓冲,例如: open('output.txt', 'w', 2**20)。使用with处理程序还应确保您的文件被刷新并关闭。

答案 3 :(得分:0)

有一种压缩阵列的方法。取决于python解释器,它可能会花费一些效率,但是在不得不求助于磁盘之前,你将能够在内存中保留更多内容。如果您在线搜索,您可能会发现其他使用压缩的筛选实现。

忽略压缩,将内存保存到磁盘的一种更简单的方法是通过内存映射文件。 Python有一个mmap模块,提供功能。您必须对原始字节进行编码,但使用struct模块则非常简单。

>>> import struct
>>> struct.pack('H', 0xcafe)
b'\xfe\xca'
>>> struct.unpack('H', b'\xfe\xca')
(51966,)