В стандартной библиотеке есть бинарный поиск для Python, в модуле bisect
. Он не поддерживает in
/contains
как есть, но вы можете написать небольшую функцию для его обработки:
from bisect import bisect_left
def contains(a, x):
"""returns true if sorted sequence `a` contains `x`"""
i = bisect_left(a, x)
return i != len(a) and a[i] == x
потом
>>> contains([1,2,3], 3)
True
>>> contains([1,2,3], 4)
False
Однако это не будет очень быстрым, так как bisect
написан на Python, а не на C, поэтому во многих случаях вы, вероятно, обнаружите, что последовательный in
быстрее. bisect
имеет необязательный C ускорение в CPython начиная с Python 2.4.
Трудно определить точную точку безубыточности в CPython. Это потому, что код написан на C; если вы проверите значение, которое больше или меньше любого значения в последовательности, то предсказание ветвления ЦП сыграет с вами злую шутку, и вы получите:
In [2]: a = list(range(100))
In [3]: %timeit contains(a, 101)
The slowest run took 8.09 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 370 ns per loop
Здесь лучшее из 3 не отражает истинное время работы алгоритма.
Но, настроив тесты, я пришел к выводу, что деление пополам может быть быстрее, чем in
для списков, содержащих всего 30 элементов.
Однако, если вы выполняете действительно много in
операций, вам следует использовать set
; вы можете преобразовать список один раз в набор (он даже не будет отсортирован), и операция in
будет асимптотически быстрее, чем любой двоичный поиск:
>>> a = [10, 6, 8, 1, 2, 5, 9]
>>> a_set = set(a)
>>> 10 in a_set
True
С другой стороны, сортировка списка требует большей временной сложности, чем создание набора, поэтому в большинстве случаев лучше всего использовать набор.
person
Antti Haapala
schedule
14.04.2016
def is_in(some_arr, val): return val in some_arr
- как, по вашему мнению, интерпретатор должен выяснить, отсортировано лиsome_arr
или нет. Ясно, что это невозможно, поэтому он не может этого сделать. Что ж, у него может быть дополнительная проверка, чтобы выяснить, отсортирован ли список, а затем использовать двоичный поиск, но, поскольку для этого требуется пройти весь список, это скорее противоречит цели. - person Voo   schedule 14.04.2016in
. Если список отсортирован, деление пополам (O(logN)) превзойдет преобразование в набор (O(N)). - person Martijn Pieters   schedule 14.04.2016bisect
имеет ускорение C. - person Martijn Pieters   schedule 14.04.2016