OILS / opy / _regtest / src / heapq.py View on Github | oilshell.org

484 lines, 210 significant
1from __future__ import print_function # for OPy compiler
2"""Heap queue algorithm (a.k.a. priority queue).
3
4Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
5all k, counting elements from 0. For the sake of comparison,
6non-existing elements are considered to be infinite. The interesting
7property of a heap is that a[0] is always its smallest element.
8
9Usage:
10
11heap = [] # creates an empty heap
12heappush(heap, item) # pushes a new item on the heap
13item = heappop(heap) # pops the smallest item from the heap
14item = heap[0] # smallest item on the heap without popping it
15heapify(x) # transforms list into a heap, in-place, in linear time
16item = heapreplace(heap, item) # pops and returns smallest item, and adds
17 # new item; the heap size is unchanged
18
19Our API differs from textbook heap algorithms as follows:
20
21- We use 0-based indexing. This makes the relationship between the
22 index for a node and the indexes for its children slightly less
23 obvious, but is more suitable since Python uses 0-based indexing.
24
25- Our heappop() method returns the smallest item, not the largest.
26
27These two make it possible to view the heap as a regular Python list
28without surprises: heap[0] is the smallest item, and heap.sort()
29maintains the heap invariant!
30"""
31
32# Original code by Kevin O'Connor, augmented by Tim Peters and Raymond Hettinger
33
34__about__ = """Heap queues
35
36[explanation by Francois Pinard]
37
38Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
39all k, counting elements from 0. For the sake of comparison,
40non-existing elements are considered to be infinite. The interesting
41property of a heap is that a[0] is always its smallest element.
42
43The strange invariant above is meant to be an efficient memory
44representation for a tournament. The numbers below are `k', not a[k]:
45
46 0
47
48 1 2
49
50 3 4 5 6
51
52 7 8 9 10 11 12 13 14
53
54 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
55
56
57In the tree above, each cell `k' is topping `2*k+1' and `2*k+2'. In
58a usual binary tournament we see in sports, each cell is the winner
59over the two cells it tops, and we can trace the winner down the tree
60to see all opponents s/he had. However, in many computer applications
61of such tournaments, we do not need to trace the history of a winner.
62To be more memory efficient, when a winner is promoted, we try to
63replace it by something else at a lower level, and the rule becomes
64that a cell and the two cells it tops contain three different items,
65but the top cell "wins" over the two topped cells.
66
67If this heap invariant is protected at all time, index 0 is clearly
68the overall winner. The simplest algorithmic way to remove it and
69find the "next" winner is to move some loser (let's say cell 30 in the
70diagram above) into the 0 position, and then percolate this new 0 down
71the tree, exchanging values, until the invariant is re-established.
72This is clearly logarithmic on the total number of items in the tree.
73By iterating over all items, you get an O(n ln n) sort.
74
75A nice feature of this sort is that you can efficiently insert new
76items while the sort is going on, provided that the inserted items are
77not "better" than the last 0'th element you extracted. This is
78especially useful in simulation contexts, where the tree holds all
79incoming events, and the "win" condition means the smallest scheduled
80time. When an event schedule other events for execution, they are
81scheduled into the future, so they can easily go into the heap. So, a
82heap is a good structure for implementing schedulers (this is what I
83used for my MIDI sequencer :-).
84
85Various structures for implementing schedulers have been extensively
86studied, and heaps are good for this, as they are reasonably speedy,
87the speed is almost constant, and the worst case is not much different
88than the average case. However, there are other representations which
89are more efficient overall, yet the worst cases might be terrible.
90
91Heaps are also very useful in big disk sorts. You most probably all
92know that a big sort implies producing "runs" (which are pre-sorted
93sequences, which size is usually related to the amount of CPU memory),
94followed by a merging passes for these runs, which merging is often
95very cleverly organised[1]. It is very important that the initial
96sort produces the longest runs possible. Tournaments are a good way
97to that. If, using all the memory available to hold a tournament, you
98replace and percolate items that happen to fit the current run, you'll
99produce runs which are twice the size of the memory for random input,
100and much better for input fuzzily ordered.
101
102Moreover, if you output the 0'th item on disk and get an input which
103may not fit in the current tournament (because the value "wins" over
104the last output value), it cannot fit in the heap, so the size of the
105heap decreases. The freed memory could be cleverly reused immediately
106for progressively building a second heap, which grows at exactly the
107same rate the first heap is melting. When the first heap completely
108vanishes, you switch heaps and start a new run. Clever and quite
109effective!
110
111In a word, heaps are useful memory structures to know. I use them in
112a few applications, and I think it is good to keep a `heap' module
113around. :-)
114
115--------------------
116[1] The disk balancing algorithms which are current, nowadays, are
117more annoying than clever, and this is a consequence of the seeking
118capabilities of the disks. On devices which cannot seek, like big
119tape drives, the story was quite different, and one had to be very
120clever to ensure (far in advance) that each tape movement will be the
121most effective possible (that is, will best participate at
122"progressing" the merge). Some tapes were even able to read
123backwards, and this was also used to avoid the rewinding time.
124Believe me, real good tape sorts were quite spectacular to watch!
125From all times, sorting has always been a Great Art! :-)
126"""
127
128__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'merge',
129 'nlargest', 'nsmallest', 'heappushpop']
130
131from itertools import islice, count, imap, izip, tee, chain
132from operator import itemgetter
133
134def cmp_lt(x, y):
135 # Use __lt__ if available; otherwise, try __le__.
136 # In Py3.x, only __lt__ will be called.
137 return (x < y) if hasattr(x, '__lt__') else (not y <= x)
138
139def heappush(heap, item):
140 """Push item onto heap, maintaining the heap invariant."""
141 heap.append(item)
142 _siftdown(heap, 0, len(heap)-1)
143
144def heappop(heap):
145 """Pop the smallest item off the heap, maintaining the heap invariant."""
146 lastelt = heap.pop() # raises appropriate IndexError if heap is empty
147 if heap:
148 returnitem = heap[0]
149 heap[0] = lastelt
150 _siftup(heap, 0)
151 else:
152 returnitem = lastelt
153 return returnitem
154
155def heapreplace(heap, item):
156 """Pop and return the current smallest value, and add the new item.
157
158 This is more efficient than heappop() followed by heappush(), and can be
159 more appropriate when using a fixed-size heap. Note that the value
160 returned may be larger than item! That constrains reasonable uses of
161 this routine unless written as part of a conditional replacement:
162
163 if item > heap[0]:
164 item = heapreplace(heap, item)
165 """
166 returnitem = heap[0] # raises appropriate IndexError if heap is empty
167 heap[0] = item
168 _siftup(heap, 0)
169 return returnitem
170
171def heappushpop(heap, item):
172 """Fast version of a heappush followed by a heappop."""
173 if heap and cmp_lt(heap[0], item):
174 item, heap[0] = heap[0], item
175 _siftup(heap, 0)
176 return item
177
178def heapify(x):
179 """Transform list into a heap, in-place, in O(len(x)) time."""
180 n = len(x)
181 # Transform bottom-up. The largest index there's any point to looking at
182 # is the largest with a child index in-range, so must have 2*i + 1 < n,
183 # or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so
184 # j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is
185 # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.
186 for i in reversed(xrange(n//2)):
187 _siftup(x, i)
188
189def _heappushpop_max(heap, item):
190 """Maxheap version of a heappush followed by a heappop."""
191 if heap and cmp_lt(item, heap[0]):
192 item, heap[0] = heap[0], item
193 _siftup_max(heap, 0)
194 return item
195
196def _heapify_max(x):
197 """Transform list into a maxheap, in-place, in O(len(x)) time."""
198 n = len(x)
199 for i in reversed(range(n//2)):
200 _siftup_max(x, i)
201
202def nlargest(n, iterable):
203 """Find the n largest elements in a dataset.
204
205 Equivalent to: sorted(iterable, reverse=True)[:n]
206 """
207 if n < 0:
208 return []
209 it = iter(iterable)
210 result = list(islice(it, n))
211 if not result:
212 return result
213 heapify(result)
214 _heappushpop = heappushpop
215 for elem in it:
216 _heappushpop(result, elem)
217 result.sort(reverse=True)
218 return result
219
220def nsmallest(n, iterable):
221 """Find the n smallest elements in a dataset.
222
223 Equivalent to: sorted(iterable)[:n]
224 """
225 if n < 0:
226 return []
227 it = iter(iterable)
228 result = list(islice(it, n))
229 if not result:
230 return result
231 _heapify_max(result)
232 _heappushpop = _heappushpop_max
233 for elem in it:
234 _heappushpop(result, elem)
235 result.sort()
236 return result
237
238# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
239# is the index of a leaf with a possibly out-of-order value. Restore the
240# heap invariant.
241def _siftdown(heap, startpos, pos):
242 newitem = heap[pos]
243 # Follow the path to the root, moving parents down until finding a place
244 # newitem fits.
245 while pos > startpos:
246 parentpos = (pos - 1) >> 1
247 parent = heap[parentpos]
248 if cmp_lt(newitem, parent):
249 heap[pos] = parent
250 pos = parentpos
251 continue
252 break
253 heap[pos] = newitem
254
255# The child indices of heap index pos are already heaps, and we want to make
256# a heap at index pos too. We do this by bubbling the smaller child of
257# pos up (and so on with that child's children, etc) until hitting a leaf,
258# then using _siftdown to move the oddball originally at index pos into place.
259#
260# We *could* break out of the loop as soon as we find a pos where newitem <=
261# both its children, but turns out that's not a good idea, and despite that
262# many books write the algorithm that way. During a heap pop, the last array
263# element is sifted in, and that tends to be large, so that comparing it
264# against values starting from the root usually doesn't pay (= usually doesn't
265# get us out of the loop early). See Knuth, Volume 3, where this is
266# explained and quantified in an exercise.
267#
268# Cutting the # of comparisons is important, since these routines have no
269# way to extract "the priority" from an array element, so that intelligence
270# is likely to be hiding in custom __cmp__ methods, or in array elements
271# storing (priority, record) tuples. Comparisons are thus potentially
272# expensive.
273#
274# On random arrays of length 1000, making this change cut the number of
275# comparisons made by heapify() a little, and those made by exhaustive
276# heappop() a lot, in accord with theory. Here are typical results from 3
277# runs (3 just to demonstrate how small the variance is):
278#
279# Compares needed by heapify Compares needed by 1000 heappops
280# -------------------------- --------------------------------
281# 1837 cut to 1663 14996 cut to 8680
282# 1855 cut to 1659 14966 cut to 8678
283# 1847 cut to 1660 15024 cut to 8703
284#
285# Building the heap by using heappush() 1000 times instead required
286# 2198, 2148, and 2219 compares: heapify() is more efficient, when
287# you can use it.
288#
289# The total compares needed by list.sort() on the same lists were 8627,
290# 8627, and 8632 (this should be compared to the sum of heapify() and
291# heappop() compares): list.sort() is (unsurprisingly!) more efficient
292# for sorting.
293
294def _siftup(heap, pos):
295 endpos = len(heap)
296 startpos = pos
297 newitem = heap[pos]
298 # Bubble up the smaller child until hitting a leaf.
299 childpos = 2*pos + 1 # leftmost child position
300 while childpos < endpos:
301 # Set childpos to index of smaller child.
302 rightpos = childpos + 1
303 if rightpos < endpos and not cmp_lt(heap[childpos], heap[rightpos]):
304 childpos = rightpos
305 # Move the smaller child up.
306 heap[pos] = heap[childpos]
307 pos = childpos
308 childpos = 2*pos + 1
309 # The leaf at pos is empty now. Put newitem there, and bubble it up
310 # to its final resting place (by sifting its parents down).
311 heap[pos] = newitem
312 _siftdown(heap, startpos, pos)
313
314def _siftdown_max(heap, startpos, pos):
315 'Maxheap variant of _siftdown'
316 newitem = heap[pos]
317 # Follow the path to the root, moving parents down until finding a place
318 # newitem fits.
319 while pos > startpos:
320 parentpos = (pos - 1) >> 1
321 parent = heap[parentpos]
322 if cmp_lt(parent, newitem):
323 heap[pos] = parent
324 pos = parentpos
325 continue
326 break
327 heap[pos] = newitem
328
329def _siftup_max(heap, pos):
330 'Maxheap variant of _siftup'
331 endpos = len(heap)
332 startpos = pos
333 newitem = heap[pos]
334 # Bubble up the larger child until hitting a leaf.
335 childpos = 2*pos + 1 # leftmost child position
336 while childpos < endpos:
337 # Set childpos to index of larger child.
338 rightpos = childpos + 1
339 if rightpos < endpos and not cmp_lt(heap[rightpos], heap[childpos]):
340 childpos = rightpos
341 # Move the larger child up.
342 heap[pos] = heap[childpos]
343 pos = childpos
344 childpos = 2*pos + 1
345 # The leaf at pos is empty now. Put newitem there, and bubble it up
346 # to its final resting place (by sifting its parents down).
347 heap[pos] = newitem
348 _siftdown_max(heap, startpos, pos)
349
350# If available, use C implementation
351try:
352 from _heapq import *
353except ImportError:
354 pass
355
356def merge(*iterables):
357 '''Merge multiple sorted inputs into a single sorted output.
358
359 Similar to sorted(itertools.chain(*iterables)) but returns a generator,
360 does not pull the data into memory all at once, and assumes that each of
361 the input streams is already sorted (smallest to largest).
362
363 >>> list(merge([1,3,5,7], [0,2,4,8], [5,10,15,20], [], [25]))
364 [0, 1, 2, 3, 4, 5, 5, 7, 8, 10, 15, 20, 25]
365
366 '''
367 _heappop, _heapreplace, _StopIteration = heappop, heapreplace, StopIteration
368 _len = len
369
370 h = []
371 h_append = h.append
372 for itnum, it in enumerate(map(iter, iterables)):
373 try:
374 next = it.next
375 h_append([next(), itnum, next])
376 except _StopIteration:
377 pass
378 heapify(h)
379
380 while _len(h) > 1:
381 try:
382 while 1:
383 v, itnum, next = s = h[0]
384 yield v
385 s[0] = next() # raises StopIteration when exhausted
386 _heapreplace(h, s) # restore heap condition
387 except _StopIteration:
388 _heappop(h) # remove empty iterator
389 if h:
390 # fast case when only a single iterator remains
391 v, itnum, next = h[0]
392 yield v
393 for v in next.__self__:
394 yield v
395
396# Extend the implementations of nsmallest and nlargest to use a key= argument
397_nsmallest = nsmallest
398def nsmallest(n, iterable, key=None):
399 """Find the n smallest elements in a dataset.
400
401 Equivalent to: sorted(iterable, key=key)[:n]
402 """
403 # Short-cut for n==1 is to use min() when len(iterable)>0
404 if n == 1:
405 it = iter(iterable)
406 head = list(islice(it, 1))
407 if not head:
408 return []
409 if key is None:
410 return [min(chain(head, it))]
411 return [min(chain(head, it), key=key)]
412
413 # When n>=size, it's faster to use sorted()
414 try:
415 size = len(iterable)
416 except (TypeError, AttributeError):
417 pass
418 else:
419 if n >= size:
420 return sorted(iterable, key=key)[:n]
421
422 # When key is none, use simpler decoration
423 if key is None:
424 it = izip(iterable, count()) # decorate
425 result = _nsmallest(n, it)
426 return map(itemgetter(0), result) # undecorate
427
428 # General case, slowest method
429 in1, in2 = tee(iterable)
430 it = izip(imap(key, in1), count(), in2) # decorate
431 result = _nsmallest(n, it)
432 return map(itemgetter(2), result) # undecorate
433
434_nlargest = nlargest
435def nlargest(n, iterable, key=None):
436 """Find the n largest elements in a dataset.
437
438 Equivalent to: sorted(iterable, key=key, reverse=True)[:n]
439 """
440
441 # Short-cut for n==1 is to use max() when len(iterable)>0
442 if n == 1:
443 it = iter(iterable)
444 head = list(islice(it, 1))
445 if not head:
446 return []
447 if key is None:
448 return [max(chain(head, it))]
449 return [max(chain(head, it), key=key)]
450
451 # When n>=size, it's faster to use sorted()
452 try:
453 size = len(iterable)
454 except (TypeError, AttributeError):
455 pass
456 else:
457 if n >= size:
458 return sorted(iterable, key=key, reverse=True)[:n]
459
460 # When key is none, use simpler decoration
461 if key is None:
462 it = izip(iterable, count(0,-1)) # decorate
463 result = _nlargest(n, it)
464 return map(itemgetter(0), result) # undecorate
465
466 # General case, slowest method
467 in1, in2 = tee(iterable)
468 it = izip(imap(key, in1), count(0,-1), in2) # decorate
469 result = _nlargest(n, it)
470 return map(itemgetter(2), result) # undecorate
471
472if __name__ == "__main__":
473 # Simple sanity test
474 heap = []
475 data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
476 for item in data:
477 heappush(heap, item)
478 sort = []
479 while heap:
480 sort.append(heappop(heap))
481 print(sort)
482
483 import doctest
484 doctest.testmod()