Spec-Zone.ru › Python 3.10

bisect — Алгоритм бисекции массива

Исходный код: Lib/bisect.py

Этот модуль предоставляет поддержку поддержания списка в отсортированном порядке без необходимости сортировки списка после каждой вставки. Для длинных списков элементов с дорогими операциями сравнения это может быть улучшением по сравнению с более распространённым подходом. Модуль называется bisect, потому что он использует базовый алгоритм бисекции для выполнения своей работы. Исходный код может быть наиболее полезен как рабочий пример алгоритма (уже учтены граничные условия!).

Предоставляются следующие функции:

bisect.bisect_left(a, x, lo=0, hi=len(a), *, key=None)

Определяет точку вставки для x в a для поддержания отсортированного порядка. Параметры lo и hi могут быть использованы для задания подмножества списка, которое должно быть рассмотрено; по умолчанию используется весь список. Если x уже присутствует в a, то точка вставки будет находиться перед (слева от) любых существующих записей. Возвращаемое значение подходит для использования в качестве первого параметра к list.insert() при условии, что a уже отсортирован.

Возвращаемая точка вставки i разбивает массив a на две половины таким образом, что all(val < x for val in a[lo : i]) для левой части и all(val >= x for val in a[i : hi]) для правой части.

key задаёт функцию-ключ от одного аргумента, используемую для извлечения ключа сравнения из каждого элемента массива. Для поддержки поиска сложных записей функция-ключ не применяется к значению x.

Если key None, то элементы сравниваются напрямую без вызова промежуточной функции.

Изменено в версии 3.10: Добавлен параметр key.

bisect.bisect_right(a, x, lo=0, hi=len(a), *, key=None)
bisect.bisect(a, x, lo=0, hi=len(a), *, key=None)

Аналогично bisect_left(), но возвращает точку вставки, которая находится после (справа от) любых существующих записей x в a.

Возвращаемая точка вставки i разбивает массив a на две половины таким образом, что all(val <= x for val in a[lo : i]) для левой части и all(val > x for val in a[i : hi]) для правой части.

key задаёт функцию-ключ от одного аргумента, используемую для извлечения ключа сравнения из каждого элемента массива. Для поддержки поиска сложных записей функция-ключ не применяется к значению x.

Если key None, то элементы сравниваются напрямую без вызова промежуточной функции.

Изменено в версии 3.10: Добавлен параметр key.

bisect.insort_left(a, x, lo=0, hi=len(a), *, key=None)

Вставляет x в a в отсортированном порядке.

Эта функция сначала выполняет bisect_left() для определения точки вставки. Затем она выполняет метод insert() для a, чтобы вставить x в соответствующее положение для поддержания сортировки.

Для поддержки вставки записей в таблицу функция-ключ (если есть) применяется к x на шаге поиска, но не на шаге вставки.

Обратите внимание, что поиск O(log n) определяется медленным шагом вставки O(n).

Изменено в версии 3.10: Добавлен параметр key.

bisect.insort_right(a, x, lo=0, hi=len(a), *, key=None)
bisect.insort(a, x, lo=0, hi=len(a), *, key=None)

Аналогично insort_left(), но вставляет x в a после любых существующих записей x.

Эта функция сначала выполняет bisect_right() для определения точки вставки. Затем она выполняет метод insert() для a, чтобы вставить x в соответствующее положение для поддержания сортировки.

Для поддержки вставки записей в таблицу функция-ключ (если есть) применяется к x на шаге поиска, но не на шаге вставки.

Обратите внимание, что поиск O(log n) определяется медленным шагом вставки O(n).

Изменено в версии 3.10: Добавлен параметр key.

Примечания по производительности

При написании чувствительной к времени кода, использующего bisect() и insort(), имейте в виду следующие моменты:

  • Бисекция эффективна для поиска диапазонов значений. Для поиска конкретных значений словари более эффективны.
  • Функции insort() являются O(n), так как шаг логарифмического поиска доминирует над линейным временем вставки.
  • Функции поиска являются бессостоятельными и отбрасывают результаты функции-ключа после их использования. Следовательно, если функции поиска используются в цикле, функция-ключ может вызываться снова и снова для одних и тех же элементов массива. Если функция-ключ не быстрая, рассмотрите возможность обернуть её с помощью functools.cache() для предотвращения дублирования вычислений. В качестве альтернативы рассмотрите возможность поиска массива предварительно вычисленных ключей для определения точки вставки (как показано в разделе примеров ниже).

См. также

  • Sorted Collections — это модуль высокой производительности, который использует bisect для управления отсортированными коллекциями данных.
  • Рецепт SortedCollection использует bisect для создания полнофункционального класса коллекции с простыми методами поиска и поддержкой функции-ключа. Ключи предварительно вычисляются для экономии ненужных вызовов функции-ключа во время поиска.

Поиск в отсортированных списках

Вышеперечисленные функции bisect() полезны для поиска точек вставки, но могут быть сложными или неудобными для использования в стандартных задачах поиска. Следующие пять функций демонстрируют, как преобразовать их в стандартные поиски для отсортированных списков:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError

def find_lt(a, x):
    'Find rightmost value less than x'
    i = bisect_left(a, x)
    if i:
        return a[i-1]
    raise ValueError

def find_le(a, x):
    'Find rightmost value less than or equal to x'
    i = bisect_right(a, x)
    if i:
        return a[i-1]
    raise ValueError

def find_gt(a, x):
    'Find leftmost value greater than x'
    i = bisect_right(a, x)
    if i != len(a):
        return a[i]
    raise ValueError

def find_ge(a, x):
    'Find leftmost item greater than or equal to x'
    i = bisect_left(a, x)
    if i != len(a):
        return a[i]
    raise ValueError

Примеры

Функция bisect() может быть полезна для числовых табличных поисков. В этом примере используется bisect() для поиска буквенной оценки за экзамен (скажем) на основе набора отсортированных числовых разрывов: 90 и выше — это «A», 80–89 — «B» и так далее:

>>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
...     i = bisect(breakpoints, score)
...     return grades[i]
...
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']

Функции bisect() и insort() также работают со списками кортежей. Аргумент key может служить для извлечения поля, используемого для упорядочения записей в таблице:

>>> from collections import namedtuple
>>> from operator import attrgetter
>>> from bisect import bisect, insort
>>> from pprint import pprint

>>> Movie = namedtuple('Movie', ('name', 'released', 'director'))

>>> movies = [
...     Movie('Jaws', 1975, 'Speilberg'),
...     Movie('Titanic', 1997, 'Cameron'),
...     Movie('The Birds', 1963, 'Hitchcock'),
...     Movie('Aliens', 1986, 'Scott')
... ]

>>> # Find the first movie released after 1960
>>> by_year = attrgetter('released')
>>> movies.sort(key=by_year)
>>> movies[bisect(movies, 1960, key=by_year)]
Movie(name='The Birds', released=1963, director='Hitchcock')

>>> # Insert a movie while maintaining sort order
>>> romance = Movie('Love Story', 1970, 'Hiller')
>>> insort(movies, romance, key=by_year)
>>> pprint(movies)
[Movie(name='The Birds', released=1963, director='Hitchcock'),
 Movie(name='Love Story', released=1970, director='Hiller'),
 Movie(name='Jaws', released=1975, director='Speilberg'),
 Movie(name='Aliens', released=1986, director='Scott'),
 Movie(name='Titanic', released=1997, director='Cameron')]

Если функция-ключ является дорогостоящей, то можно избежать повторных вызовов функций, выполнив поиск в списке предварительно вычисленных ключей для нахождения индекса записи:

>>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
>>> data.sort(key=lambda r: r[1])       # Or use operator.itemgetter(1).
>>> keys = [r[1] for r in data]         # Precompute a list of keys.
>>> data[bisect_left(keys, 0)]
('black', 0)
>>> data[bisect_left(keys, 1)]
('blue', 1)
>>> data[bisect_left(keys, 5)]
('red', 5)
>>> data[bisect_left(keys, 8)]
('yellow', 8)

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/bisect.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API