【发布时间】:2011-12-17 23:12:44
【问题描述】:
我正在进行蒙特卡罗模拟。作为这项任务的一部分,我生成均匀分布在间隔(0,100) 上的样本。
generate = lambda: uniform(0,100)
当所有最接近的生成点对满足条件时,迭代停止。
check = lambda a,b: True if (b-a)<5 else False
我需要有一些结构来有效地保留所有生成的点并能够按升序遍历它们以在所有后续对上执行check。
Python 中有一个heapq 模块,它支持非常有效的堆结构。我决定使用它。
我遇到了一个问题。我发现这个模块不支持遍历过程。我发现按升序访问堆值的唯一方法是使用heapq.heappop。但它会从堆中删除值。
我找到了解决方法,只是将堆对象复制到新对象中,并在新对象上使用heappop 进行迭代。但是我认为每次迭代都将整个结构复制到内存中并不是很有效。
我还有其他方法可以更有效地做我想做的事情吗?
用于说明的简化代码。
import heapq
from random import uniform
from itertools import tee, izip, count
from copy import copy
def pairwise(iterable): #get values from iterator in pairs
a, b = tee(iterable)
next(b, None)
return izip(a, b)
check = lambda a,b: True if (b-a)<5 else False
generate = lambda: uniform(0,100)
def iterate_heap(heap):
heap = copy(heap) #Here I have to copy the heap to be able to traverse
try:
while True:
yield heapq.heappop(heap)
except IndexError:
return
def trial():
items = []
for i in count():
item = generate()
heapq.heappush(items, item)
it = iterate_heap(items)
it = pairwise(it)
if i>0 and all(check(a,b) for a,b in it): #if i==0 then 'it' returns no values and 'all' returns True
return i
print "The solution is reached. It took %d iterations." % trial()
paiwise 函数来自here 的配方。
更新:
在这个使用heappop 的实现中,每次迭代的复杂度是O(n*log(n)):
复制堆:O(n)
向堆中添加新值:O(log(n))
遍历:n 元素 * O(log(n)) 从堆中弹出每个值 -> O(n*log(n))。
结果:O(n+log(n)+n*log(n)) = O(n*log(n)
但我希望遍历是O(n),因此产生的复杂度将是O(n)。
顺便说一下,如果我们只使用排序列表,我们需要在每次添加时对列表进行排序,所以O(n*log(n)),但遍历将是n*O(1) -> O(n)。所以,得到的复杂度仍然是O(n*log(n))。
我找到了解决办法。这是使用bisect 模块。找到要添加的位置是O(log(n))。添加到列表中的是O(n)(由于实现,插入到位后的所有值都必须移动)。遍历是O(n)。因此,得到的复杂度为O(n)。
不过,如果有一种方法可以在 Python 中使用堆来解决此任务,我会感到很困惑。
【问题讨论】:
-
随机注释:
True if (b-a)<5 else False仅相当于(b - a) < 5。 -
@JochenRitzel 是的,你是对的。我将“堆”与“二叉搜索树”混淆了。所以,是的,没有理由使用堆。
-
您对 bisect 的计算是错误的。用 bisect 构造一个大小为 n 的排序列表需要 O(log(n)) per insert,所以它也是 O(nlog(n))。将其与移动列表部分的开销相乘,甚至是 O(nn*log(n))
-
你是对的,查找和插入需要 O(logn+n)。尽管如此,这样做 n 次会给你带来 O(nn) 的复杂性。 B-Tree 在 O(nlogn) 内完成。