Модуль bisect - сортировка в моменте

Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs

Смотреть материал на YouTube | RuTube

Стандартная библиотека языка Python содержит модуль bisect, который позволяет поддерживать в отсортированном порядке любую изменяемую упорядоченную последовательность. Чаще всего – это списки языка Python. Давайте посмотрим на конкретном примере, как это работает.

Предположим, имеется пустой список:

sd = []

в который нужно последовательно занести следующие числа:

data = (4, 3, -1, 0, 10, 5, 4)

так, чтобы они располагались по возрастанию (неубыванию) своих значений. Если это делать стандартными методами языка Python, используя сортировку после каждого нового добавления:

for x in data:
    sd.append(x)
    sd.sort()
    print(sd)

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

for x in data:
    bisect.insort(sd, x)
    print(sd)

Функция insort вначале анализирует данные списка sd, вычисляет индекс позиции добавляемого элемента x, и сразу ставит новый элемент так, чтобы сохранялся порядок сортировки. В итоге в консоли увидим следующий процесс формирования списка sd:

[4]
[3, 4]
[-1, 3, 4]
[-1, 0, 3, 4]
[-1, 0, 3, 4, 10]
[-1, 0, 3, 4, 5, 10]
[-1, 0, 3, 4, 4, 5, 10]

Мало того, с помощью функции bisect() можно отдельно вычислить индекс добавляемого элемента:

for x in data:
    pos = bisect.bisect(sd, x)
    bisect.insort(sd, x)
    print(pos, sd)

Получим:

0 [4]
0 [3, 4]
0 [-1, 3, 4]
1 [-1, 0, 3, 4]
4 [-1, 0, 3, 4, 10]
4 [-1, 0, 3, 4, 5, 10]
4 [-1, 0, 3, 4, 4, 5, 10]

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

На самом деле функции insort() и bisect() – это псевдонимы (алиасы) функций:

  • insort_right(list, x[, lo=0, hi=len(a)]) – вставка элемента x справа от повторяющихся значений;
  • bisect_right(list, x[, lo=0, hi=len(a)]) – определение позиции вставляемого значения x справа от повторяющихся значений.

Например, при выполнении программы:

data = (2, 3, 4, 2, 3, 4)
sd = []
 
for x in data:
    pos = bisect.bisect_right(sd, x)
    bisect.insort_right(sd, x)
    print(pos, sd)

Увидим следующие позиции вставки элементов с дублями:

0 [2]
1 [2, 3]
2 [2, 3, 4]
1 [2, 2, 3, 4]
3 [2, 2, 3, 3, 4]
5 [2, 2, 3, 3, 4, 4]

А если заменить методы bisect_right() и insort_right() на bisect_left() и insort_left():

for x in data:
    pos = bisect.bisect_left(sd, x)
    bisect.insort_left(sd, x)
    print(pos, sd)

то увидим немного другой результат:

0 [2]
1 [2, 3]
2 [2, 3, 4]
0 [2, 2, 3, 4]
2 [2, 2, 3, 3, 4]
4 [2, 2, 3, 3, 4, 4]

Теперь дублирующие значения вставляются слева от соответствующих повторяющихся цепочек данных.

Итак, использование модуля bisect позволяет значительно повысить производительность операций для поддержания порядка сортировки длинных упорядоченных последовательностей. Короткие последовательности (до 100 элементов) можно каждый раз сортировать и обычным методом sort(). Это не сильно скажется на объеме вычислений. Однако при длинных списках использование модуля bisect позволяет значительно повышать производительность, особенно если операция сравнения двух элементов списка требует значительных вычислительных затрат.

Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs

Видео по теме