Home | History | Annotate | Download | only in library
      1 :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 (a] acm.org>
      7 .. sectionauthor:: Raymond Hettinger <python at rcn.com>
      8 .. example based on the PyModules FAQ entry by Aaron Watters <arw (a] pythonpros.com>
      9 
     10 **Source code:** :source:`Lib/bisect.py`
     11 
     12 --------------
     13 
     14 This module provides support for maintaining a list in sorted order without
     15 having to sort the list after each insertion.  For long lists of items with
     16 expensive comparison operations, this can be an improvement over the more common
     17 approach.  The module is called :mod:`bisect` because it uses a basic bisection
     18 algorithm to do its work.  The source code may be most useful as a working
     19 example of the algorithm (the boundary conditions are already right!).
     20 
     21 The following functions are provided:
     22 
     23 
     24 .. function:: bisect_left(a, x, lo=0, hi=len(a))
     25 
     26    Locate the insertion point for *x* in *a* to maintain sorted order.
     27    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
     31    parameter to ``list.insert()`` assuming that *a* is already sorted.
     32 
     33    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.
     36 
     37 .. function:: bisect_right(a, x, lo=0, hi=len(a))
     38               bisect(a, x, lo=0, hi=len(a))
     39 
     40    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*.
     42 
     43    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.
     46 
     47 .. function:: insort_left(a, x, lo=0, hi=len(a))
     48 
     49    Insert *x* in *a* in sorted order.  This is equivalent to
     50    ``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.
     53 
     54 .. function:: insort_right(a, x, lo=0, hi=len(a))
     55               insort(a, x, lo=0, hi=len(a))
     56 
     57    Similar to :func:`insort_left`, but inserting *x* in *a* after any existing
     58    entries of *x*.
     59 
     60 .. seealso::
     61 
     62    `SortedCollection recipe
     63    <https://code.activestate.com/recipes/577197-sortedcollection/>`_ that uses
     64    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 
     68 
     69 Searching Sorted Lists
     70 ----------------------
     71 
     72 The above :func:`bisect` functions are useful for finding insertion points but
     73 can be tricky or awkward to use for common searching tasks. The following five
     74 functions show how to transform them into the standard lookups for sorted
     75 lists::
     76 
     77     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
     83 
     84     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):
    102             return a[i]
    103         raise ValueError
    104 
    105     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):
    109             return a[i]
    110         raise ValueError
    111 
    112 
    113 Other Examples
    114 --------------
    115 
    116 .. _bisect-example:
    117 
    118 The :func:`bisect` function can be useful for numeric table lookups. This
    119 example uses :func:`bisect` to look up a letter grade for an exam score (say)
    120 based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
    121 a 'B', and so on::
    122 
    123    >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    124    ...     i = bisect(breakpoints, score)
    125    ...     return grades[i]
    126    ...
    127    >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
    128    ['F', 'A', 'C', 'C', 'B', 'A', 'A']
    129 
    130 Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect`
    131 functions to have *key* or *reversed* arguments because that would lead to an
    132 inefficient design (successive calls to bisect functions would not "remember"
    133 all of the previous key lookups).
    134 
    135 Instead, it is better to search a list of precomputed keys to find the index
    136 of the record in question::
    137 
    138     >>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
    139     >>> data.sort(key=lambda r: r[1])
    140     >>> keys = [r[1] for r in data]         # precomputed list of keys
    141     >>> 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)
    149 
    150