blob: 6bf7814b257f4abdbe48b48614635313fdbb07d7 [file] [log] [blame]
Georg Brandl116aa622007-08-15 14:28:22 +00001:mod:`bisect` --- Array bisection algorithm
2===========================================
3
4.. module:: bisect
5 :synopsis: Array bisection algorithms for binary searching.
6.. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org>
Raymond Hettinger20933e02010-09-01 06:58:25 +00007.. sectionauthor:: Raymond Hettinger <python at rcn.com>
Christian Heimes5b5e81c2007-12-31 16:14:33 +00008.. example based on the PyModules FAQ entry by Aaron Watters <arw@pythonpros.com>
Georg Brandl116aa622007-08-15 14:28:22 +00009
Raymond Hettinger10480942011-01-10 03:26:08 +000010**Source code:** :source:`Lib/bisect.py`
11
Raymond Hettinger4f707fd2011-01-10 19:54:11 +000012--------------
13
Georg Brandl116aa622007-08-15 14:28:22 +000014This module provides support for maintaining a list in sorted order without
15having to sort the list after each insertion. For long lists of items with
16expensive comparison operations, this can be an improvement over the more common
17approach. The module is called :mod:`bisect` because it uses a basic bisection
18algorithm to do its work. The source code may be most useful as a working
19example of the algorithm (the boundary conditions are already right!).
20
21The following functions are provided:
22
23
Georg Brandl0d8f0732009-04-05 22:20:44 +000024.. function:: bisect_left(a, x, lo=0, hi=len(a))
Georg Brandl116aa622007-08-15 14:28:22 +000025
Raymond Hettinger20933e02010-09-01 06:58:25 +000026 Locate the insertion point for *x* in *a* to maintain sorted order.
Georg Brandl0d8f0732009-04-05 22:20:44 +000027 The parameters *lo* and *hi* may be used to specify a subset of the list
28 which should be considered; by default the entire list is used. If *x* is
29 already present in *a*, the insertion point will be before (to the left of)
30 any existing entries. The return value is suitable for use as the first
Raymond Hettinger20933e02010-09-01 06:58:25 +000031 parameter to ``list.insert()`` assuming that *a* is already sorted.
Georg Brandl116aa622007-08-15 14:28:22 +000032
Raymond Hettinger20933e02010-09-01 06:58:25 +000033 The returned insertion point *i* partitions the array *a* into two halves so
34 that ``all(val < x for val in a[lo:i])`` for the left side and
35 ``all(val >= x for val in a[i:hi])`` for the right side.
Georg Brandl116aa622007-08-15 14:28:22 +000036
Georg Brandl0d8f0732009-04-05 22:20:44 +000037.. function:: bisect_right(a, x, lo=0, hi=len(a))
38 bisect(a, x, lo=0, hi=len(a))
Georg Brandl116aa622007-08-15 14:28:22 +000039
Georg Brandl0d8f0732009-04-05 22:20:44 +000040 Similar to :func:`bisect_left`, but returns an insertion point which comes
41 after (to the right of) any existing entries of *x* in *a*.
Georg Brandl116aa622007-08-15 14:28:22 +000042
Raymond Hettinger20933e02010-09-01 06:58:25 +000043 The returned insertion point *i* partitions the array *a* into two halves so
44 that ``all(val <= x for val in a[lo:i])`` for the left side and
45 ``all(val > x for val in a[i:hi])`` for the right side.
Georg Brandl116aa622007-08-15 14:28:22 +000046
Georg Brandl0d8f0732009-04-05 22:20:44 +000047.. function:: insort_left(a, x, lo=0, hi=len(a))
Georg Brandl116aa622007-08-15 14:28:22 +000048
Georg Brandl0d8f0732009-04-05 22:20:44 +000049 Insert *x* in *a* in sorted order. This is equivalent to
Raymond Hettinger20933e02010-09-01 06:58:25 +000050 ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is
51 already sorted. Keep in mind that the O(log n) search is dominated by
52 the slow O(n) insertion step.
Georg Brandl116aa622007-08-15 14:28:22 +000053
Georg Brandl0d8f0732009-04-05 22:20:44 +000054.. function:: insort_right(a, x, lo=0, hi=len(a))
55 insort(a, x, lo=0, hi=len(a))
Georg Brandl116aa622007-08-15 14:28:22 +000056
Georg Brandl0d8f0732009-04-05 22:20:44 +000057 Similar to :func:`insort_left`, but inserting *x* in *a* after any existing
58 entries of *x*.
Georg Brandl116aa622007-08-15 14:28:22 +000059
Raymond Hettinger20933e02010-09-01 06:58:25 +000060.. seealso::
61
62 `SortedCollection recipe
Serhiy Storchaka6dff0202016-05-07 10:49:07 +030063 <https://code.activestate.com/recipes/577197-sortedcollection/>`_ that uses
Raymond Hettinger20933e02010-09-01 06:58:25 +000064 bisect to build a full-featured collection class with straight-forward search
65 methods and support for a key-function. The keys are precomputed to save
66 unnecessary calls to the key function during searches.
67
Georg Brandl116aa622007-08-15 14:28:22 +000068
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +000069Searching Sorted Lists
70----------------------
71
Raymond Hettinger20933e02010-09-01 06:58:25 +000072The above :func:`bisect` functions are useful for finding insertion points but
73can be tricky or awkward to use for common searching tasks. The following five
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +000074functions show how to transform them into the standard lookups for sorted
75lists::
76
Raymond Hettinger20933e02010-09-01 06:58:25 +000077 def index(a, x):
78 'Locate the leftmost value exactly equal to x'
79 i = bisect_left(a, x)
80 if i != len(a) and a[i] == x:
81 return i
82 raise ValueError
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +000083
Raymond Hettinger20933e02010-09-01 06:58:25 +000084 def find_lt(a, x):
85 'Find rightmost value less than x'
86 i = bisect_left(a, x)
87 if i:
88 return a[i-1]
89 raise ValueError
90
91 def find_le(a, x):
92 'Find rightmost value less than or equal to x'
93 i = bisect_right(a, x)
94 if i:
95 return a[i-1]
96 raise ValueError
97
98 def find_gt(a, x):
99 'Find leftmost value greater than x'
100 i = bisect_right(a, x)
101 if i != len(a):
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000102 return a[i]
Raymond Hettinger20933e02010-09-01 06:58:25 +0000103 raise ValueError
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000104
Raymond Hettinger20933e02010-09-01 06:58:25 +0000105 def find_ge(a, x):
106 'Find leftmost item greater than or equal to x'
107 i = bisect_left(a, x)
108 if i != len(a):
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000109 return a[i]
Raymond Hettinger20933e02010-09-01 06:58:25 +0000110 raise ValueError
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000111
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000112
113Other Examples
114--------------
Georg Brandl116aa622007-08-15 14:28:22 +0000115
116.. _bisect-example:
117
Raymond Hettinger20933e02010-09-01 06:58:25 +0000118The :func:`bisect` function can be useful for numeric table lookups. This
119example uses :func:`bisect` to look up a letter grade for an exam score (say)
120based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
121a 'B', and so on::
Georg Brandl116aa622007-08-15 14:28:22 +0000122
Raymond Hettinger20933e02010-09-01 06:58:25 +0000123 >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
124 ... i = bisect(breakpoints, score)
125 ... return grades[i]
Georg Brandl116aa622007-08-15 14:28:22 +0000126 ...
Raymond Hettinger20933e02010-09-01 06:58:25 +0000127 >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
128 ['F', 'A', 'C', 'C', 'B', 'A', 'A']
Georg Brandl116aa622007-08-15 14:28:22 +0000129
Raymond Hettingere046d2a2009-06-11 22:01:24 +0000130Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect`
131functions to have *key* or *reversed* arguments because that would lead to an
Georg Brandl6faee4e2010-09-21 14:48:28 +0000132inefficient design (successive calls to bisect functions would not "remember"
Raymond Hettingere046d2a2009-06-11 22:01:24 +0000133all of the previous key lookups).
Georg Brandl116aa622007-08-15 14:28:22 +0000134
Raymond Hettingere046d2a2009-06-11 22:01:24 +0000135Instead, it is better to search a list of precomputed keys to find the index
136of the record in question::
137
138 >>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
Raymond Hettinger27352a52009-06-11 22:06:06 +0000139 >>> data.sort(key=lambda r: r[1])
140 >>> keys = [r[1] for r in data] # precomputed list of keys
Raymond Hettingere046d2a2009-06-11 22:01:24 +0000141 >>> data[bisect_left(keys, 0)]
142 ('black', 0)
143 >>> data[bisect_left(keys, 1)]
144 ('blue', 1)
145 >>> data[bisect_left(keys, 5)]
146 ('red', 5)
147 >>> data[bisect_left(keys, 8)]
148 ('yellow', 8)
Raymond Hettinger87c9d6c2010-08-07 07:36:55 +0000149