Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 51k
add exponential search algorithm#10732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
703f3b8dbbe1351d79bd0ff30ae0f66e56616f58e126b528363bf03a4ca8a6ab524404dc9155aafbd458d8dfe7466a03cbd76119bdc32f860b53cdd49744fbFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,9 @@ | ||
| #!/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 | ||
| For manual testing run: | ||
| @@ -34,16 +34,12 @@ def bisect_left( | ||
| Examples: | ||
| >>> bisect_left([0, 5, 7, 10, 15], 0) | ||
| 0 | ||
| >>> bisect_left([0, 5, 7, 10, 15], 6) | ||
| 2 | ||
| >>> bisect_left([0, 5, 7, 10, 15], 20) | ||
| 5 | ||
| >>> bisect_left([0, 5, 7, 10, 15], 15, 1, 3) | ||
| 3 | ||
| >>> bisect_left([0, 5, 7, 10, 15], 6, 2) | ||
| 2 | ||
| """ | ||
| @@ -79,16 +75,12 @@ def bisect_right( | ||
| Examples: | ||
| >>> bisect_right([0, 5, 7, 10, 15], 0) | ||
| 1 | ||
| >>> bisect_right([0, 5, 7, 10, 15], 15) | ||
| 5 | ||
| >>> bisect_right([0, 5, 7, 10, 15], 6) | ||
| 2 | ||
| >>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3) | ||
| 3 | ||
| >>> bisect_right([0, 5, 7, 10, 15], 6, 2) | ||
| 2 | ||
| """ | ||
| @@ -124,7 +116,6 @@ def insort_left( | ||
| >>> insort_left(sorted_collection, 6) | ||
| >>> sorted_collection | ||
| [0, 5, 6, 7, 10, 15] | ||
| >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] | ||
| >>> item = (5, 5) | ||
| >>> insort_left(sorted_collection, item) | ||
| @@ -134,12 +125,10 @@ def insort_left( | ||
| True | ||
| >>> item is sorted_collection[2] | ||
| False | ||
| >>> sorted_collection = [0, 5, 7, 10, 15] | ||
| >>> insort_left(sorted_collection, 20) | ||
| >>> sorted_collection | ||
| [0, 5, 7, 10, 15, 20] | ||
| >>> sorted_collection = [0, 5, 7, 10, 15] | ||
| >>> insort_left(sorted_collection, 15, 1, 3) | ||
| >>> sorted_collection | ||
| @@ -167,7 +156,6 @@ def insort_right( | ||
| >>> insort_right(sorted_collection, 6) | ||
| >>> sorted_collection | ||
| [0, 5, 6, 7, 10, 15] | ||
| >>> sorted_collection = [(0, 0), (5, 5), (7, 7), (10, 10), (15, 15)] | ||
| >>> item = (5, 5) | ||
| >>> insort_right(sorted_collection, item) | ||
| @@ -177,12 +165,10 @@ def insort_right( | ||
| False | ||
| >>> item is sorted_collection[2] | ||
| True | ||
| >>> sorted_collection = [0, 5, 7, 10, 15] | ||
| >>> insort_right(sorted_collection, 20) | ||
| >>> sorted_collection | ||
| [0, 5, 7, 10, 15, 20] | ||
| >>> sorted_collection = [0, 5, 7, 10, 15] | ||
| >>> insort_right(sorted_collection, 15, 1, 3) | ||
| >>> sorted_collection | ||
| @@ -191,29 +177,28 @@ def insort_right( | ||
| sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) | ||
| def binary_search(sorted_collection: list[int], item: int) -> int | None: | ||
| """Pure implementation of binary search algorithm in Python | ||
| def binary_search(sorted_collection: list[int], item: int) -> int: | ||
| """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 | ||
| :param sorted_collection: some ascending sorted collection with comparable items | ||
| :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: | ||
| >>> binary_search([0, 5, 7, 10, 15], 0) | ||
| 0 | ||
| >>> binary_search([0, 5, 7, 10, 15], 15) | ||
| 4 | ||
| >>> binary_search([0, 5, 7, 10, 15], 5) | ||
| 1 | ||
| >>> 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 | ||
| right = len(sorted_collection) - 1 | ||
| @@ -226,66 +211,66 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None: | ||
| right = midpoint - 1 | ||
| else: | ||
| left = midpoint + 1 | ||
| return None | ||
| return -1 | ||
| def binary_search_std_lib(sorted_collection: list[int], item: int) -> int | None: | ||
| """Pure implementation of binary search algorithm in Python using stdlib | ||
| def binary_search_std_lib(sorted_collection: list[int], item: int) -> int: | ||
| """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 | ||
| :param sorted_collection: some ascending sorted collection with comparable items | ||
| :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: | ||
| >>> binary_search_std_lib([0, 5, 7, 10, 15], 0) | ||
| 0 | ||
| >>> binary_search_std_lib([0, 5, 7, 10, 15], 15) | ||
| 4 | ||
| >>> binary_search_std_lib([0, 5, 7, 10, 15], 5) | ||
| 1 | ||
| >>> 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) | ||
| if index != len(sorted_collection) and sorted_collection[index] == item: | ||
| return index | ||
| return None | ||
| return -1 | ||
| def binary_search_by_recursion( | ||
| sorted_collection: list[int], item: int, left: int, right: int | ||
| ) -> int | None: | ||
| """Pure implementation of binary search algorithm in Python by recursion | ||
| sorted_collection: list[int], item: int, left: int = 0, right: int = -1 | ||
| ) -> int: | ||
| """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 | ||
| 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 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: | ||
| >>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4) | ||
| 0 | ||
| >>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4) | ||
| 4 | ||
| >>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4) | ||
| 1 | ||
| >>> 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: | ||
| return None | ||
| return -1 | ||
| 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) | ||
| 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( | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tianyizheng02 | ||
| 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__": | ||
| 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(",")) | ||
| target = int(input("Enter a single number to be found in the list:\n")) | ||
| result = binary_search(collection, target) | ||
| if result is None: | ||
| target = int(input("Enter a single number to be found in the list:")) | ||
| result = binary_search(sorted_collection=collection, item=target) | ||
| if result == -1: | ||
| print(f"{target} was not found in {collection}.") | ||
| else: | ||
| print(f"{target} was found at position {result} in {collection}.") | ||
| print(f"{target} was found at position {result} of {collection}.") | ||
Uh oh!
There was an error while loading. Please reload this page.