Skip to content

Commit b814cf3

Browse files
kiarash8112pre-commit-ci[bot]cclaussuser
authored
add exponential search algorithm (TheAlgorithms#10732)
* add exponential_search algorithm * replace binary_search with binary_search_recursion * convert left type to int to be useable in binary_search_recursion * add docs and tests for exponential_search algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * move exponential_search to binary_search.py to pass github auto build tests delete exponential_search.py file * Update searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * remove additional space searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * return single data type in exponential_search searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * add doctest mod searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use // instread of int() convert searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * change test according to new code searches/binary_search.py Co-authored-by: Christian Clauss <[email protected]> * fix binary_search_recursion multiple type return error * add a timeit benchmark for exponential_search * sort input of binary search to be equal in performance test with exponential_search * raise value error instead of sorting input in binary and exonential search to fix bugs * Update binary_search.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <[email protected]> Co-authored-by: user <[email protected]>
1 parent 06edc0e commit b814cf3

File tree

1 file changed

+100
-49
lines changed

1 file changed

+100
-49
lines changed

searches/binary_search.py

+100-49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env python3
22

33
"""
4-
This is pure Python implementation of binary search algorithms
4+
Pure Python implementations of binary search algorithms
55
6-
For doctests run following command:
6+
For doctests run the following command:
77
python3 -m doctest -v binary_search.py
88
99
For manual testing run:
@@ -34,16 +34,12 @@ def bisect_left(
3434
Examples:
3535
>>> bisect_left([0, 5, 7, 10, 15], 0)
3636
0
37-
3837
>>> bisect_left([0, 5, 7, 10, 15], 6)
3938
2
40-
4139
>>> bisect_left([0, 5, 7, 10, 15], 20)
4240
5
43-
4441
>>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3)
4542
3
46-
4743
>>> bisect_left([0, 5, 7, 10, 15], 6, 2)
4844
2
4945
"""
@@ -79,16 +75,12 @@ def bisect_right(
7975
Examples:
8076
>>> bisect_right([0, 5, 7, 10, 15], 0)
8177
1
82-
8378
>>> bisect_right([0, 5, 7, 10, 15], 15)
8479
5
85-
8680
>>> bisect_right([0, 5, 7, 10, 15], 6)
8781
2
88-
8982
>>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
9083
3
91-
9284
>>> bisect_right([0, 5, 7, 10, 15], 6, 2)
9385
2
9486
"""
@@ -124,7 +116,6 @@ def insort_left(
124116
>>> insort_left(sorted_collection, 6)
125117
>>> sorted_collection
126118
[0, 5, 6, 7, 10, 15]
127-
128119
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
129120
>>> item = (5, 5)
130121
>>> insort_left(sorted_collection, item)
@@ -134,12 +125,10 @@ def insort_left(
134125
True
135126
>>> item is sorted_collection[2]
136127
False
137-
138128
>>> sorted_collection = [0, 5, 7, 10, 15]
139129
>>> insort_left(sorted_collection, 20)
140130
>>> sorted_collection
141131
[0, 5, 7, 10, 15, 20]
142-
143132
>>> sorted_collection = [0, 5, 7, 10, 15]
144133
>>> insort_left(sorted_collection, 15, 1, 3)
145134
>>> sorted_collection
@@ -167,7 +156,6 @@ def insort_right(
167156
>>> insort_right(sorted_collection, 6)
168157
>>> sorted_collection
169158
[0, 5, 6, 7, 10, 15]
170-
171159
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
172160
>>> item = (5, 5)
173161
>>> insort_right(sorted_collection, item)
@@ -177,12 +165,10 @@ def insort_right(
177165
False
178166
>>> item is sorted_collection[2]
179167
True
180-
181168
>>> sorted_collection = [0, 5, 7, 10, 15]
182169
>>> insort_right(sorted_collection, 20)
183170
>>> sorted_collection
184171
[0, 5, 7, 10, 15, 20]
185-
186172
>>> sorted_collection = [0, 5, 7, 10, 15]
187173
>>> insort_right(sorted_collection, 15, 1, 3)
188174
>>> sorted_collection
@@ -191,29 +177,28 @@ def insort_right(
191177
sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item)
192178

193179

194-
def binary_search(sorted_collection: list[int], item: int) -> int | None:
195-
"""Pure implementation of binary search algorithm in Python
180+
def binary_search(sorted_collection: list[int], item: int) -> int:
181+
"""Pure implementation of a binary search algorithm in Python
196182
197-
Be careful collection must be ascending sorted, otherwise result will be
183+
Be careful collection must be ascending sorted otherwise, the result will be
198184
unpredictable
199185
200186
:param sorted_collection: some ascending sorted collection with comparable items
201187
:param item: item value to search
202-
:return: index of found item or None if item is not found
188+
:return: index of the found item or -1 if the item is not found
203189
204190
Examples:
205191
>>> binary_search([0, 5, 7, 10, 15], 0)
206192
0
207-
208193
>>> binary_search([0, 5, 7, 10, 15], 15)
209194
4
210-
211195
>>> binary_search([0, 5, 7, 10, 15], 5)
212196
1
213-
214197
>>> binary_search([0, 5, 7, 10, 15], 6)
215-
198+
-1
216199
"""
200+
if list(sorted_collection) != sorted(sorted_collection):
201+
raise ValueError("sorted_collection must be sorted in ascending order")
217202
left = 0
218203
right = len(sorted_collection) - 1
219204

@@ -226,66 +211,66 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None:
226211
right = midpoint - 1
227212
else:
228213
left = midpoint + 1
229-
return None
214+
return -1
230215

231216

232-
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int | None:
233-
"""Pure implementation of binary search algorithm in Python using stdlib
217+
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
218+
"""Pure implementation of a binary search algorithm in Python using stdlib
234219
235-
Be careful collection must be ascending sorted, otherwise result will be
220+
Be careful collection must be ascending sorted otherwise, the result will be
236221
unpredictable
237222
238223
:param sorted_collection: some ascending sorted collection with comparable items
239224
:param item: item value to search
240-
:return: index of found item or None if item is not found
225+
:return: index of the found item or -1 if the item is not found
241226
242227
Examples:
243228
>>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
244229
0
245-
246230
>>> binary_search_std_lib([0, 5, 7, 10, 15], 15)
247231
4
248-
249232
>>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
250233
1
251-
252234
>>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
253-
235+
-1
254236
"""
237+
if list(sorted_collection) != sorted(sorted_collection):
238+
raise ValueError("sorted_collection must be sorted in ascending order")
255239
index = bisect.bisect_left(sorted_collection, item)
256240
if index != len(sorted_collection) and sorted_collection[index] == item:
257241
return index
258-
return None
242+
return -1
259243

260244

261245
def binary_search_by_recursion(
262-
sorted_collection: list[int], item: int, left: int, right: int
263-
) -> int | None:
264-
"""Pure implementation of binary search algorithm in Python by recursion
246+
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
247+
) -> int:
248+
"""Pure implementation of a binary search algorithm in Python by recursion
265249
266-
Be careful collection must be ascending sorted, otherwise result will be
250+
Be careful collection must be ascending sorted otherwise, the result will be
267251
unpredictable
268252
First recursion should be started with left=0 and right=(len(sorted_collection)-1)
269253
270254
:param sorted_collection: some ascending sorted collection with comparable items
271255
:param item: item value to search
272-
:return: index of found item or None if item is not found
256+
:return: index of the found item or -1 if the item is not found
273257
274258
Examples:
275259
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
276260
0
277-
278261
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
279262
4
280-
281263
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
282264
1
283-
284265
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
285-
266+
-1
286267
"""
268+
if right < 0:
269+
right = len(sorted_collection) - 1
270+
if list(sorted_collection) != sorted(sorted_collection):
271+
raise ValueError("sorted_collection must be sorted in ascending order")
287272
if right < left:
288-
return None
273+
return -1
289274

290275
midpoint = left + (right - left) // 2
291276

@@ -297,12 +282,78 @@ def binary_search_by_recursion(
297282
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
298283

299284

285+
def exponential_search(sorted_collection: list[int], item: int) -> int:
286+
"""Pure implementation of an exponential search algorithm in Python
287+
Resources used:
288+
https://en.wikipedia.org/wiki/Exponential_search
289+
290+
Be careful collection must be ascending sorted otherwise, result will be
291+
unpredictable
292+
293+
:param sorted_collection: some ascending sorted collection with comparable items
294+
:param item: item value to search
295+
:return: index of the found item or -1 if the item is not found
296+
297+
the order of this algorithm is O(lg I) where I is index position of item if exist
298+
299+
Examples:
300+
>>> exponential_search([0, 5, 7, 10, 15], 0)
301+
0
302+
>>> exponential_search([0, 5, 7, 10, 15], 15)
303+
4
304+
>>> exponential_search([0, 5, 7, 10, 15], 5)
305+
1
306+
>>> exponential_search([0, 5, 7, 10, 15], 6)
307+
-1
308+
"""
309+
if list(sorted_collection) != sorted(sorted_collection):
310+
raise ValueError("sorted_collection must be sorted in ascending order")
311+
bound = 1
312+
while bound < len(sorted_collection) and sorted_collection[bound] < item:
313+
bound *= 2
314+
left = bound // 2
315+
right = min(bound, len(sorted_collection) - 1)
316+
last_result = binary_search_by_recursion(
317+
sorted_collection=sorted_collection, item=item, left=left, right=right
318+
)
319+
if last_result is None:
320+
return -1
321+
return last_result
322+
323+
324+
searches = ( # Fastest to slowest...
325+
binary_search_std_lib,
326+
binary_search,
327+
exponential_search,
328+
binary_search_by_recursion,
329+
)
330+
331+
300332
if __name__ == "__main__":
301-
user_input = input("Enter numbers separated by comma:\n").strip()
333+
import doctest
334+
import timeit
335+
336+
doctest.testmod()
337+
for search in searches:
338+
name = f"{search.__name__:>26}"
339+
print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") # type: ignore[operator]
340+
341+
print("\nBenchmarks...")
342+
setup = "collection = range(1000)"
343+
for search in searches:
344+
name = search.__name__
345+
print(
346+
f"{name:>26}:",
347+
timeit.timeit(
348+
f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals()
349+
),
350+
)
351+
352+
user_input = input("\nEnter numbers separated by comma: ").strip()
302353
collection = sorted(int(item) for item in user_input.split(","))
303-
target = int(input("Enter a single number to be found in the list:\n"))
304-
result = binary_search(collection, target)
305-
if result is None:
354+
target = int(input("Enter a single number to be found in the list: "))
355+
result = binary_search(sorted_collection=collection, item=target)
356+
if result == -1:
306357
print(f"{target} was not found in {collection}.")
307358
else:
308-
print(f"{target} was found at position {result} in {collection}.")
359+
print(f"{target} was found at position {result} of {collection}.")

0 commit comments

Comments
 (0)