TheAlgorithms-Python
360 строк · 11.4 Кб
1#!/usr/bin/env python3
2
3"""
4Pure Python implementations of binary search algorithms
5
6For doctests run the following command:
7python3 -m doctest -v binary_search.py
8
9For manual testing run:
10python3 binary_search.py
11"""
12
13from __future__ import annotations14
15import bisect16
17
18def bisect_left(19sorted_collection: list[int], item: int, lo: int = 0, hi: int = -120) -> int:21"""22Locates the first element in a sorted array that is larger or equal to a given
23value.
24
25It has the same interface as
26https://docs.python.org/3/library/bisect.html#bisect.bisect_left .
27
28:param sorted_collection: some ascending sorted collection with comparable items
29:param item: item to bisect
30:param lo: lowest index to consider (as in sorted_collection[lo:hi])
31:param hi: past the highest index to consider (as in sorted_collection[lo:hi])
32:return: index i such that all values in sorted_collection[lo:i] are < item and all
33values in sorted_collection[i:hi] are >= item.
34
35Examples:
36>>> bisect_left([0, 5, 7, 10, 15], 0)
370
38>>> bisect_left([0, 5, 7, 10, 15], 6)
392
40>>> bisect_left([0, 5, 7, 10, 15], 20)
415
42>>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3)
433
44>>> bisect_left([0, 5, 7, 10, 15], 6, 2)
452
46"""
47if hi < 0:48hi = len(sorted_collection)49
50while lo < hi:51mid = lo + (hi - lo) // 252if sorted_collection[mid] < item:53lo = mid + 154else:55hi = mid56
57return lo58
59
60def bisect_right(61sorted_collection: list[int], item: int, lo: int = 0, hi: int = -162) -> int:63"""64Locates the first element in a sorted array that is larger than a given value.
65
66It has the same interface as
67https://docs.python.org/3/library/bisect.html#bisect.bisect_right .
68
69:param sorted_collection: some ascending sorted collection with comparable items
70:param item: item to bisect
71:param lo: lowest index to consider (as in sorted_collection[lo:hi])
72:param hi: past the highest index to consider (as in sorted_collection[lo:hi])
73:return: index i such that all values in sorted_collection[lo:i] are <= item and
74all values in sorted_collection[i:hi] are > item.
75
76Examples:
77>>> bisect_right([0, 5, 7, 10, 15], 0)
781
79>>> bisect_right([0, 5, 7, 10, 15], 15)
805
81>>> bisect_right([0, 5, 7, 10, 15], 6)
822
83>>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
843
85>>> bisect_right([0, 5, 7, 10, 15], 6, 2)
862
87"""
88if hi < 0:89hi = len(sorted_collection)90
91while lo < hi:92mid = lo + (hi - lo) // 293if sorted_collection[mid] <= item:94lo = mid + 195else:96hi = mid97
98return lo99
100
101def insort_left(102sorted_collection: list[int], item: int, lo: int = 0, hi: int = -1103) -> None:104"""105Inserts a given value into a sorted array before other values with the same value.
106
107It has the same interface as
108https://docs.python.org/3/library/bisect.html#bisect.insort_left .
109
110:param sorted_collection: some ascending sorted collection with comparable items
111:param item: item to insert
112:param lo: lowest index to consider (as in sorted_collection[lo:hi])
113:param hi: past the highest index to consider (as in sorted_collection[lo:hi])
114
115Examples:
116>>> sorted_collection = [0, 5, 7, 10, 15]
117>>> insort_left(sorted_collection, 6)
118>>> sorted_collection
119[0, 5, 6, 7, 10, 15]
120>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
121>>> item = (5, 5)
122>>> insort_left(sorted_collection, item)
123>>> sorted_collection
124[(0, 0), (5, 5), (5, 5), (7, 7), (10, 10), (15, 15)]
125>>> item is sorted_collection[1]
126True
127>>> item is sorted_collection[2]
128False
129>>> sorted_collection = [0, 5, 7, 10, 15]
130>>> insort_left(sorted_collection, 20)
131>>> sorted_collection
132[0, 5, 7, 10, 15, 20]
133>>> sorted_collection = [0, 5, 7, 10, 15]
134>>> insort_left(sorted_collection, 15, 1, 3)
135>>> sorted_collection
136[0, 5, 7, 15, 10, 15]
137"""
138sorted_collection.insert(bisect_left(sorted_collection, item, lo, hi), item)139
140
141def insort_right(142sorted_collection: list[int], item: int, lo: int = 0, hi: int = -1143) -> None:144"""145Inserts a given value into a sorted array after other values with the same value.
146
147It has the same interface as
148https://docs.python.org/3/library/bisect.html#bisect.insort_right .
149
150:param sorted_collection: some ascending sorted collection with comparable items
151:param item: item to insert
152:param lo: lowest index to consider (as in sorted_collection[lo:hi])
153:param hi: past the highest index to consider (as in sorted_collection[lo:hi])
154
155Examples:
156>>> sorted_collection = [0, 5, 7, 10, 15]
157>>> insort_right(sorted_collection, 6)
158>>> sorted_collection
159[0, 5, 6, 7, 10, 15]
160>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
161>>> item = (5, 5)
162>>> insort_right(sorted_collection, item)
163>>> sorted_collection
164[(0, 0), (5, 5), (5, 5), (7, 7), (10, 10), (15, 15)]
165>>> item is sorted_collection[1]
166False
167>>> item is sorted_collection[2]
168True
169>>> sorted_collection = [0, 5, 7, 10, 15]
170>>> insort_right(sorted_collection, 20)
171>>> sorted_collection
172[0, 5, 7, 10, 15, 20]
173>>> sorted_collection = [0, 5, 7, 10, 15]
174>>> insort_right(sorted_collection, 15, 1, 3)
175>>> sorted_collection
176[0, 5, 7, 15, 10, 15]
177"""
178sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item)179
180
181def binary_search(sorted_collection: list[int], item: int) -> int:182"""Pure implementation of a binary search algorithm in Python183
184Be careful collection must be ascending sorted otherwise, the result will be
185unpredictable
186
187:param sorted_collection: some ascending sorted collection with comparable items
188:param item: item value to search
189:return: index of the found item or -1 if the item is not found
190
191Examples:
192>>> binary_search([0, 5, 7, 10, 15], 0)
1930
194>>> binary_search([0, 5, 7, 10, 15], 15)
1954
196>>> binary_search([0, 5, 7, 10, 15], 5)
1971
198>>> binary_search([0, 5, 7, 10, 15], 6)
199-1
200"""
201if list(sorted_collection) != sorted(sorted_collection):202raise ValueError("sorted_collection must be sorted in ascending order")203left = 0204right = len(sorted_collection) - 1205
206while left <= right:207midpoint = left + (right - left) // 2208current_item = sorted_collection[midpoint]209if current_item == item:210return midpoint211elif item < current_item:212right = midpoint - 1213else:214left = midpoint + 1215return -1216
217
218def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:219"""Pure implementation of a binary search algorithm in Python using stdlib220
221Be careful collection must be ascending sorted otherwise, the result will be
222unpredictable
223
224:param sorted_collection: some ascending sorted collection with comparable items
225:param item: item value to search
226:return: index of the found item or -1 if the item is not found
227
228Examples:
229>>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
2300
231>>> binary_search_std_lib([0, 5, 7, 10, 15], 15)
2324
233>>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
2341
235>>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
236-1
237"""
238if list(sorted_collection) != sorted(sorted_collection):239raise ValueError("sorted_collection must be sorted in ascending order")240index = bisect.bisect_left(sorted_collection, item)241if index != len(sorted_collection) and sorted_collection[index] == item:242return index243return -1244
245
246def binary_search_by_recursion(247sorted_collection: list[int], item: int, left: int = 0, right: int = -1248) -> int:249"""Pure implementation of a binary search algorithm in Python by recursion250
251Be careful collection must be ascending sorted otherwise, the result will be
252unpredictable
253First recursion should be started with left=0 and right=(len(sorted_collection)-1)
254
255:param sorted_collection: some ascending sorted collection with comparable items
256:param item: item value to search
257:return: index of the found item or -1 if the item is not found
258
259Examples:
260>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
2610
262>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
2634
264>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
2651
266>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
267-1
268"""
269if right < 0:270right = len(sorted_collection) - 1271if list(sorted_collection) != sorted(sorted_collection):272raise ValueError("sorted_collection must be sorted in ascending order")273if right < left:274return -1275
276midpoint = left + (right - left) // 2277
278if sorted_collection[midpoint] == item:279return midpoint280elif sorted_collection[midpoint] > item:281return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)282else:283return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)284
285
286def exponential_search(sorted_collection: list[int], item: int) -> int:287"""Pure implementation of an exponential search algorithm in Python288Resources used:
289https://en.wikipedia.org/wiki/Exponential_search
290
291Be careful collection must be ascending sorted otherwise, result will be
292unpredictable
293
294:param sorted_collection: some ascending sorted collection with comparable items
295:param item: item value to search
296:return: index of the found item or -1 if the item is not found
297
298the order of this algorithm is O(lg I) where I is index position of item if exist
299
300Examples:
301>>> exponential_search([0, 5, 7, 10, 15], 0)
3020
303>>> exponential_search([0, 5, 7, 10, 15], 15)
3044
305>>> exponential_search([0, 5, 7, 10, 15], 5)
3061
307>>> exponential_search([0, 5, 7, 10, 15], 6)
308-1
309"""
310if list(sorted_collection) != sorted(sorted_collection):311raise ValueError("sorted_collection must be sorted in ascending order")312bound = 1313while bound < len(sorted_collection) and sorted_collection[bound] < item:314bound *= 2315left = bound // 2316right = min(bound, len(sorted_collection) - 1)317last_result = binary_search_by_recursion(318sorted_collection=sorted_collection, item=item, left=left, right=right319)320if last_result is None:321return -1322return last_result323
324
325searches = ( # Fastest to slowest...326binary_search_std_lib,327binary_search,328exponential_search,329binary_search_by_recursion,330)
331
332
333if __name__ == "__main__":334import doctest335import timeit336
337doctest.testmod()338for search in searches:339name = f"{search.__name__:>26}"340print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") # type: ignore[operator]341
342print("\nBenchmarks...")343setup = "collection = range(1000)"344for search in searches:345name = search.__name__346print(347f"{name:>26}:",348timeit.timeit(349f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals()350),351)352
353user_input = input("\nEnter numbers separated by comma: ").strip()354collection = sorted(int(item) for item in user_input.split(","))355target = int(input("Enter a single number to be found in the list: "))356result = binary_search(sorted_collection=collection, item=target)357if result == -1:358print(f"{target} was not found in {collection}.")359else:360print(f"{target} was found at position {result} of {collection}.")361