mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11:08 +00:00
add exponential search algorithm (#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 <cclauss@me.com> * remove additional space searches/binary_search.py Co-authored-by: Christian Clauss <cclauss@me.com> * return single data type in exponential_search searches/binary_search.py Co-authored-by: Christian Clauss <cclauss@me.com> * add doctest mod searches/binary_search.py Co-authored-by: Christian Clauss <cclauss@me.com> * [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 <cclauss@me.com> * change test according to new code searches/binary_search.py Co-authored-by: Christian Clauss <cclauss@me.com> * 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 <cclauss@me.com> Co-authored-by: user <user@kali.user>
This commit is contained in:
parent
06edc0eea0
commit
b814cf3781
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is pure Python implementation of binary search algorithms
|
Pure Python implementations of binary search algorithms
|
||||||
|
|
||||||
For doctests run following command:
|
For doctests run the following command:
|
||||||
python3 -m doctest -v binary_search.py
|
python3 -m doctest -v binary_search.py
|
||||||
|
|
||||||
For manual testing run:
|
For manual testing run:
|
||||||
|
@ -34,16 +34,12 @@ def bisect_left(
|
||||||
Examples:
|
Examples:
|
||||||
>>> bisect_left([0, 5, 7, 10, 15], 0)
|
>>> bisect_left([0, 5, 7, 10, 15], 0)
|
||||||
0
|
0
|
||||||
|
|
||||||
>>> bisect_left([0, 5, 7, 10, 15], 6)
|
>>> bisect_left([0, 5, 7, 10, 15], 6)
|
||||||
2
|
2
|
||||||
|
|
||||||
>>> bisect_left([0, 5, 7, 10, 15], 20)
|
>>> bisect_left([0, 5, 7, 10, 15], 20)
|
||||||
5
|
5
|
||||||
|
|
||||||
>>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3)
|
>>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3)
|
||||||
3
|
3
|
||||||
|
|
||||||
>>> bisect_left([0, 5, 7, 10, 15], 6, 2)
|
>>> bisect_left([0, 5, 7, 10, 15], 6, 2)
|
||||||
2
|
2
|
||||||
"""
|
"""
|
||||||
|
@ -79,16 +75,12 @@ def bisect_right(
|
||||||
Examples:
|
Examples:
|
||||||
>>> bisect_right([0, 5, 7, 10, 15], 0)
|
>>> bisect_right([0, 5, 7, 10, 15], 0)
|
||||||
1
|
1
|
||||||
|
|
||||||
>>> bisect_right([0, 5, 7, 10, 15], 15)
|
>>> bisect_right([0, 5, 7, 10, 15], 15)
|
||||||
5
|
5
|
||||||
|
|
||||||
>>> bisect_right([0, 5, 7, 10, 15], 6)
|
>>> bisect_right([0, 5, 7, 10, 15], 6)
|
||||||
2
|
2
|
||||||
|
|
||||||
>>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
|
>>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
|
||||||
3
|
3
|
||||||
|
|
||||||
>>> bisect_right([0, 5, 7, 10, 15], 6, 2)
|
>>> bisect_right([0, 5, 7, 10, 15], 6, 2)
|
||||||
2
|
2
|
||||||
"""
|
"""
|
||||||
|
@ -124,7 +116,6 @@ def insort_left(
|
||||||
>>> insort_left(sorted_collection, 6)
|
>>> insort_left(sorted_collection, 6)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
[0, 5, 6, 7, 10, 15]
|
[0, 5, 6, 7, 10, 15]
|
||||||
|
|
||||||
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
|
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
|
||||||
>>> item = (5, 5)
|
>>> item = (5, 5)
|
||||||
>>> insort_left(sorted_collection, item)
|
>>> insort_left(sorted_collection, item)
|
||||||
|
@ -134,12 +125,10 @@ def insort_left(
|
||||||
True
|
True
|
||||||
>>> item is sorted_collection[2]
|
>>> item is sorted_collection[2]
|
||||||
False
|
False
|
||||||
|
|
||||||
>>> sorted_collection = [0, 5, 7, 10, 15]
|
>>> sorted_collection = [0, 5, 7, 10, 15]
|
||||||
>>> insort_left(sorted_collection, 20)
|
>>> insort_left(sorted_collection, 20)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
[0, 5, 7, 10, 15, 20]
|
[0, 5, 7, 10, 15, 20]
|
||||||
|
|
||||||
>>> sorted_collection = [0, 5, 7, 10, 15]
|
>>> sorted_collection = [0, 5, 7, 10, 15]
|
||||||
>>> insort_left(sorted_collection, 15, 1, 3)
|
>>> insort_left(sorted_collection, 15, 1, 3)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
|
@ -167,7 +156,6 @@ def insort_right(
|
||||||
>>> insort_right(sorted_collection, 6)
|
>>> insort_right(sorted_collection, 6)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
[0, 5, 6, 7, 10, 15]
|
[0, 5, 6, 7, 10, 15]
|
||||||
|
|
||||||
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
|
>>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)]
|
||||||
>>> item = (5, 5)
|
>>> item = (5, 5)
|
||||||
>>> insort_right(sorted_collection, item)
|
>>> insort_right(sorted_collection, item)
|
||||||
|
@ -177,12 +165,10 @@ def insort_right(
|
||||||
False
|
False
|
||||||
>>> item is sorted_collection[2]
|
>>> item is sorted_collection[2]
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> sorted_collection = [0, 5, 7, 10, 15]
|
>>> sorted_collection = [0, 5, 7, 10, 15]
|
||||||
>>> insort_right(sorted_collection, 20)
|
>>> insort_right(sorted_collection, 20)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
[0, 5, 7, 10, 15, 20]
|
[0, 5, 7, 10, 15, 20]
|
||||||
|
|
||||||
>>> sorted_collection = [0, 5, 7, 10, 15]
|
>>> sorted_collection = [0, 5, 7, 10, 15]
|
||||||
>>> insort_right(sorted_collection, 15, 1, 3)
|
>>> insort_right(sorted_collection, 15, 1, 3)
|
||||||
>>> sorted_collection
|
>>> sorted_collection
|
||||||
|
@ -191,29 +177,28 @@ def insort_right(
|
||||||
sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item)
|
sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item)
|
||||||
|
|
||||||
|
|
||||||
def binary_search(sorted_collection: list[int], item: int) -> int | None:
|
def binary_search(sorted_collection: list[int], item: int) -> int:
|
||||||
"""Pure implementation of binary search algorithm in Python
|
"""Pure implementation of a binary search algorithm in Python
|
||||||
|
|
||||||
Be careful collection must be ascending sorted, otherwise result will be
|
Be careful collection must be ascending sorted otherwise, the result will be
|
||||||
unpredictable
|
unpredictable
|
||||||
|
|
||||||
:param sorted_collection: some ascending sorted collection with comparable items
|
:param sorted_collection: some ascending sorted collection with comparable items
|
||||||
:param item: item value to search
|
:param item: item value to search
|
||||||
:return: index of found item or None if item is not found
|
:return: index of the found item or -1 if the item is not found
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> binary_search([0, 5, 7, 10, 15], 0)
|
>>> binary_search([0, 5, 7, 10, 15], 0)
|
||||||
0
|
0
|
||||||
|
|
||||||
>>> binary_search([0, 5, 7, 10, 15], 15)
|
>>> binary_search([0, 5, 7, 10, 15], 15)
|
||||||
4
|
4
|
||||||
|
|
||||||
>>> binary_search([0, 5, 7, 10, 15], 5)
|
>>> binary_search([0, 5, 7, 10, 15], 5)
|
||||||
1
|
1
|
||||||
|
|
||||||
>>> binary_search([0, 5, 7, 10, 15], 6)
|
>>> binary_search([0, 5, 7, 10, 15], 6)
|
||||||
|
-1
|
||||||
"""
|
"""
|
||||||
|
if list(sorted_collection) != sorted(sorted_collection):
|
||||||
|
raise ValueError("sorted_collection must be sorted in ascending order")
|
||||||
left = 0
|
left = 0
|
||||||
right = len(sorted_collection) - 1
|
right = len(sorted_collection) - 1
|
||||||
|
|
||||||
|
@ -226,66 +211,66 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None:
|
||||||
right = midpoint - 1
|
right = midpoint - 1
|
||||||
else:
|
else:
|
||||||
left = midpoint + 1
|
left = midpoint + 1
|
||||||
return None
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int | None:
|
def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
|
||||||
"""Pure implementation of binary search algorithm in Python using stdlib
|
"""Pure implementation of a binary search algorithm in Python using stdlib
|
||||||
|
|
||||||
Be careful collection must be ascending sorted, otherwise result will be
|
Be careful collection must be ascending sorted otherwise, the result will be
|
||||||
unpredictable
|
unpredictable
|
||||||
|
|
||||||
:param sorted_collection: some ascending sorted collection with comparable items
|
:param sorted_collection: some ascending sorted collection with comparable items
|
||||||
:param item: item value to search
|
:param item: item value to search
|
||||||
:return: index of found item or None if item is not found
|
:return: index of the found item or -1 if the item is not found
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
|
>>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
|
||||||
0
|
0
|
||||||
|
|
||||||
>>> binary_search_std_lib([0, 5, 7, 10, 15], 15)
|
>>> binary_search_std_lib([0, 5, 7, 10, 15], 15)
|
||||||
4
|
4
|
||||||
|
|
||||||
>>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
|
>>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
|
||||||
1
|
1
|
||||||
|
|
||||||
>>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
|
>>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
|
||||||
|
-1
|
||||||
"""
|
"""
|
||||||
|
if list(sorted_collection) != sorted(sorted_collection):
|
||||||
|
raise ValueError("sorted_collection must be sorted in ascending order")
|
||||||
index = bisect.bisect_left(sorted_collection, item)
|
index = bisect.bisect_left(sorted_collection, item)
|
||||||
if index != len(sorted_collection) and sorted_collection[index] == item:
|
if index != len(sorted_collection) and sorted_collection[index] == item:
|
||||||
return index
|
return index
|
||||||
return None
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def binary_search_by_recursion(
|
def binary_search_by_recursion(
|
||||||
sorted_collection: list[int], item: int, left: int, right: int
|
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
|
||||||
) -> int | None:
|
) -> int:
|
||||||
"""Pure implementation of binary search algorithm in Python by recursion
|
"""Pure implementation of a binary search algorithm in Python by recursion
|
||||||
|
|
||||||
Be careful collection must be ascending sorted, otherwise result will be
|
Be careful collection must be ascending sorted otherwise, the result will be
|
||||||
unpredictable
|
unpredictable
|
||||||
First recursion should be started with left=0 and right=(len(sorted_collection)-1)
|
First recursion should be started with left=0 and right=(len(sorted_collection)-1)
|
||||||
|
|
||||||
:param sorted_collection: some ascending sorted collection with comparable items
|
:param sorted_collection: some ascending sorted collection with comparable items
|
||||||
:param item: item value to search
|
:param item: item value to search
|
||||||
:return: index of found item or None if item is not found
|
:return: index of the found item or -1 if the item is not found
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
|
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
|
||||||
0
|
0
|
||||||
|
|
||||||
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
|
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
|
||||||
4
|
4
|
||||||
|
|
||||||
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
|
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
|
||||||
1
|
1
|
||||||
|
|
||||||
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
|
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
|
||||||
|
-1
|
||||||
"""
|
"""
|
||||||
|
if right < 0:
|
||||||
|
right = len(sorted_collection) - 1
|
||||||
|
if list(sorted_collection) != sorted(sorted_collection):
|
||||||
|
raise ValueError("sorted_collection must be sorted in ascending order")
|
||||||
if right < left:
|
if right < left:
|
||||||
return None
|
return -1
|
||||||
|
|
||||||
midpoint = left + (right - left) // 2
|
midpoint = left + (right - left) // 2
|
||||||
|
|
||||||
|
@ -297,12 +282,78 @@ def binary_search_by_recursion(
|
||||||
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
|
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
|
||||||
|
|
||||||
|
|
||||||
|
def exponential_search(sorted_collection: list[int], item: int) -> int:
|
||||||
|
"""Pure implementation of an exponential search algorithm in Python
|
||||||
|
Resources used:
|
||||||
|
https://en.wikipedia.org/wiki/Exponential_search
|
||||||
|
|
||||||
|
Be careful collection must be ascending sorted otherwise, result will be
|
||||||
|
unpredictable
|
||||||
|
|
||||||
|
:param sorted_collection: some ascending sorted collection with comparable items
|
||||||
|
:param item: item value to search
|
||||||
|
:return: index of the found item or -1 if the item is not found
|
||||||
|
|
||||||
|
the order of this algorithm is O(lg I) where I is index position of item if exist
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> exponential_search([0, 5, 7, 10, 15], 0)
|
||||||
|
0
|
||||||
|
>>> exponential_search([0, 5, 7, 10, 15], 15)
|
||||||
|
4
|
||||||
|
>>> exponential_search([0, 5, 7, 10, 15], 5)
|
||||||
|
1
|
||||||
|
>>> exponential_search([0, 5, 7, 10, 15], 6)
|
||||||
|
-1
|
||||||
|
"""
|
||||||
|
if list(sorted_collection) != sorted(sorted_collection):
|
||||||
|
raise ValueError("sorted_collection must be sorted in ascending order")
|
||||||
|
bound = 1
|
||||||
|
while bound < len(sorted_collection) and sorted_collection[bound] < item:
|
||||||
|
bound *= 2
|
||||||
|
left = bound // 2
|
||||||
|
right = min(bound, len(sorted_collection) - 1)
|
||||||
|
last_result = binary_search_by_recursion(
|
||||||
|
sorted_collection=sorted_collection, item=item, left=left, right=right
|
||||||
|
)
|
||||||
|
if last_result is None:
|
||||||
|
return -1
|
||||||
|
return last_result
|
||||||
|
|
||||||
|
|
||||||
|
searches = ( # Fastest to slowest...
|
||||||
|
binary_search_std_lib,
|
||||||
|
binary_search,
|
||||||
|
exponential_search,
|
||||||
|
binary_search_by_recursion,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
user_input = input("Enter numbers separated by comma:\n").strip()
|
import doctest
|
||||||
|
import timeit
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
for search in searches:
|
||||||
|
name = f"{search.__name__:>26}"
|
||||||
|
print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }") # type: ignore[operator]
|
||||||
|
|
||||||
|
print("\nBenchmarks...")
|
||||||
|
setup = "collection = range(1000)"
|
||||||
|
for search in searches:
|
||||||
|
name = search.__name__
|
||||||
|
print(
|
||||||
|
f"{name:>26}:",
|
||||||
|
timeit.timeit(
|
||||||
|
f"{name}(collection, 500)", setup=setup, number=5_000, globals=globals()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
user_input = input("\nEnter numbers separated by comma: ").strip()
|
||||||
collection = sorted(int(item) for item in user_input.split(","))
|
collection = sorted(int(item) for item in user_input.split(","))
|
||||||
target = int(input("Enter a single number to be found in the list:\n"))
|
target = int(input("Enter a single number to be found in the list: "))
|
||||||
result = binary_search(collection, target)
|
result = binary_search(sorted_collection=collection, item=target)
|
||||||
if result is None:
|
if result == -1:
|
||||||
print(f"{target} was not found in {collection}.")
|
print(f"{target} was not found in {collection}.")
|
||||||
else:
|
else:
|
||||||
print(f"{target} was found at position {result} in {collection}.")
|
print(f"{target} was found at position {result} of {collection}.")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user