blob: 48cb3310a23747858aa09019f2a222690d86a0d1 [file] [log] [blame]
Georg Brandl8ec7f652007-08-15 14:28:01 +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 Hettinger54f824f2010-09-01 19:42:36 +00007.. sectionauthor:: Raymond Hettinger <python at rcn.com>
Georg Brandlb19be572007-12-29 10:57:00 +00008.. example based on the PyModules FAQ entry by Aaron Watters <arw@pythonpros.com>
Georg Brandl8ec7f652007-08-15 14:28:01 +00009
10This module provides support for maintaining a list in sorted order without
11having to sort the list after each insertion. For long lists of items with
12expensive comparison operations, this can be an improvement over the more common
13approach. The module is called :mod:`bisect` because it uses a basic bisection
14algorithm to do its work. The source code may be most useful as a working
15example of the algorithm (the boundary conditions are already right!).
16
Raymond Hettinger47ed1c12010-08-07 21:55:06 +000017.. versionadded:: 2.1
18
Raymond Hettingere0e08222010-11-06 07:10:31 +000019.. seealso::
20
21 Latest version of the `bisect module Python source code
22 <http://svn.python.org/view/python/branches/release27-maint/Lib/bisect.py?view=markup>`_
23
Georg Brandl8ec7f652007-08-15 14:28:01 +000024The following functions are provided:
25
26
Raymond Hettinger54f824f2010-09-01 19:42:36 +000027.. function:: bisect_left(a, x, lo=0, hi=len(a))
Georg Brandl8ec7f652007-08-15 14:28:01 +000028
Raymond Hettinger54f824f2010-09-01 19:42:36 +000029 Locate the insertion point for *x* in *a* to maintain sorted order.
30 The parameters *lo* and *hi* may be used to specify a subset of the list
31 which should be considered; by default the entire list is used. If *x* is
32 already present in *a*, the insertion point will be before (to the left of)
33 any existing entries. The return value is suitable for use as the first
34 parameter to ``list.insert()`` assuming that *a* is already sorted.
Georg Brandl8ec7f652007-08-15 14:28:01 +000035
Raymond Hettinger54f824f2010-09-01 19:42:36 +000036 The returned insertion point *i* partitions the array *a* into two halves so
37 that ``all(val < x for val in a[lo:i])`` for the left side and
38 ``all(val >= x for val in a[i:hi])`` for the right side.
Georg Brandl8ec7f652007-08-15 14:28:01 +000039
Raymond Hettinger54f824f2010-09-01 19:42:36 +000040.. function:: bisect_right(a, x, lo=0, hi=len(a))
41 bisect(a, x, lo=0, hi=len(a))
Georg Brandl8ec7f652007-08-15 14:28:01 +000042
Raymond Hettinger54f824f2010-09-01 19:42:36 +000043 Similar to :func:`bisect_left`, but returns an insertion point which comes
44 after (to the right of) any existing entries of *x* in *a*.
Georg Brandl8ec7f652007-08-15 14:28:01 +000045
Raymond Hettinger54f824f2010-09-01 19:42:36 +000046 The returned insertion point *i* partitions the array *a* into two halves so
47 that ``all(val <= x for val in a[lo:i])`` for the left side and
48 ``all(val > x for val in a[i:hi])`` for the right side.
Georg Brandl8ec7f652007-08-15 14:28:01 +000049
Raymond Hettinger54f824f2010-09-01 19:42:36 +000050.. function:: insort_left(a, x, lo=0, hi=len(a))
Georg Brandl8ec7f652007-08-15 14:28:01 +000051
Raymond Hettinger54f824f2010-09-01 19:42:36 +000052 Insert *x* in *a* in sorted order. This is equivalent to
53 ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is
54 already sorted. Keep in mind that the O(log n) search is dominated by
55 the slow O(n) insertion step.
Georg Brandl8ec7f652007-08-15 14:28:01 +000056
Raymond Hettinger54f824f2010-09-01 19:42:36 +000057.. function:: insort_right(a, x, lo=0, hi=len(a))
Raymond Hettinger47ed1c12010-08-07 21:55:06 +000058 insort(a, x, lo=0, hi=len(a))
Georg Brandl8ec7f652007-08-15 14:28:01 +000059
Raymond Hettinger54f824f2010-09-01 19:42:36 +000060 Similar to :func:`insort_left`, but inserting *x* in *a* after any existing
61 entries of *x*.
Georg Brandl8ec7f652007-08-15 14:28:01 +000062
Raymond Hettinger54f824f2010-09-01 19:42:36 +000063.. seealso::
64
65 `SortedCollection recipe
66 <http://code.activestate.com/recipes/577197-sortedcollection/>`_ that uses
67 bisect to build a full-featured collection class with straight-forward search
68 methods and support for a key-function. The keys are precomputed to save
69 unnecessary calls to the key function during searches.
70
Georg Brandl8ec7f652007-08-15 14:28:01 +000071
Raymond Hettinger47ed1c12010-08-07 21:55:06 +000072Searching Sorted Lists
73----------------------
Georg Brandl8ec7f652007-08-15 14:28:01 +000074
Raymond Hettinger54f824f2010-09-01 19:42:36 +000075The above :func:`bisect` functions are useful for finding insertion points but
76can be tricky or awkward to use for common searching tasks. The following five
Raymond Hettinger47ed1c12010-08-07 21:55:06 +000077functions show how to transform them into the standard lookups for sorted
78lists::
Georg Brandl8ec7f652007-08-15 14:28:01 +000079
Raymond Hettinger54f824f2010-09-01 19:42:36 +000080 def index(a, x):
81 'Locate the leftmost value exactly equal to x'
82 i = bisect_left(a, x)
83 if i != len(a) and a[i] == x:
84 return i
85 raise ValueError
Georg Brandl8ec7f652007-08-15 14:28:01 +000086
Raymond Hettinger54f824f2010-09-01 19:42:36 +000087 def find_lt(a, x):
88 'Find rightmost value less than x'
89 i = bisect_left(a, x)
90 if i:
91 return a[i-1]
92 raise ValueError
93
94 def find_le(a, x):
95 'Find rightmost value less than or equal to x'
96 i = bisect_right(a, x)
97 if i:
98 return a[i-1]
99 raise ValueError
100
101 def find_gt(a, x):
102 'Find leftmost value greater than x'
103 i = bisect_right(a, x)
104 if i != len(a):
Raymond Hettinger47ed1c12010-08-07 21:55:06 +0000105 return a[i]
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000106 raise ValueError
Georg Brandl8ec7f652007-08-15 14:28:01 +0000107
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000108 def find_ge(a, x):
109 'Find leftmost item greater than or equal to x'
110 i = bisect_left(a, x)
111 if i != len(a):
Raymond Hettinger47ed1c12010-08-07 21:55:06 +0000112 return a[i]
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000113 raise ValueError
Raymond Hettinger47ed1c12010-08-07 21:55:06 +0000114
Raymond Hettinger47ed1c12010-08-07 21:55:06 +0000115
116Other Examples
117--------------
Georg Brandl8ec7f652007-08-15 14:28:01 +0000118
119.. _bisect-example:
120
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000121The :func:`bisect` function can be useful for numeric table lookups. This
122example uses :func:`bisect` to look up a letter grade for an exam score (say)
123based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
124a 'B', and so on::
Georg Brandl8ec7f652007-08-15 14:28:01 +0000125
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000126 >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
127 ... i = bisect(breakpoints, score)
128 ... return grades[i]
Georg Brandl8ec7f652007-08-15 14:28:01 +0000129 ...
Raymond Hettinger54f824f2010-09-01 19:42:36 +0000130 >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
131 ['F', 'A', 'C', 'C', 'B', 'A', 'A']
Georg Brandl8ec7f652007-08-15 14:28:01 +0000132
Raymond Hettinger87be88c2009-06-11 22:04:00 +0000133Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect`
134functions to have *key* or *reversed* arguments because that would lead to an
Georg Brandl09302282010-10-06 09:32:48 +0000135inefficient design (successive calls to bisect functions would not "remember"
Raymond Hettinger87be88c2009-06-11 22:04:00 +0000136all of the previous key lookups).
Georg Brandl8ec7f652007-08-15 14:28:01 +0000137
Raymond Hettinger87be88c2009-06-11 22:04:00 +0000138Instead, it is better to search a list of precomputed keys to find the index
139of the record in question::
140
141 >>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
Raymond Hettinger2742e7e2009-06-11 22:08:10 +0000142 >>> data.sort(key=lambda r: r[1])
143 >>> keys = [r[1] for r in data] # precomputed list of keys
Raymond Hettinger87be88c2009-06-11 22:04:00 +0000144 >>> data[bisect_left(keys, 0)]
145 ('black', 0)
146 >>> data[bisect_left(keys, 1)]
147 ('blue', 1)
148 >>> data[bisect_left(keys, 5)]
149 ('red', 5)
150 >>> data[bisect_left(keys, 8)]
151 ('yellow', 8)
Raymond Hettinger47ed1c12010-08-07 21:55:06 +0000152