我已经实现了Sieve of Atkin,它的效果非常接近100,000,000左右。除此之外,它因内存问题而崩溃。
在算法中,我想用基于硬盘的阵列替换基于内存的阵列。 Python" wb"文件功能和Seek功能可以解决问题。在我发明新轮子之前,有人可以提供建议吗?一开始就出现两个问题:
为什么我这样做?寻找娱乐和保持面条工作的老geezer。
答案 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**2
,3*x**2+y**2
和3*x**2-y**2
。一种方法是首先计算它们然后对数字进行排序,有一些排序算法在驱动器存储上运行良好(仍为O(N log N)),但这会损害时间复杂度。一种更好的方法是迭代x
和y
,使得您一次在一个块上运行,因为一个块由一个间隔确定,例如,您可以简单地迭代所有{ {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)