From 703f3b8f19a450715ec66c7f5e1592ad242f6b29 Mon Sep 17 00:00:00 2001 From: kiarash8112 Date: Fri, 20 Oct 2023 14:13:34 -0400 Subject: [PATCH 01/18] add exponential_search algorithm --- searches/exponential_search.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 searches/exponential_search.py diff --git a/searches/exponential_search.py b/searches/exponential_search.py new file mode 100644 index 000000000000..36c32f4509fb --- /dev/null +++ b/searches/exponential_search.py @@ -0,0 +1,17 @@ +from binary_search import binary_search + + +def exponential_search(sorted_collection: list[int], item: int) -> int | None: + bound = 1 + while bound < len(sorted_collection) and sorted_collection[bound] < item: + bound *= 2 + if bound < len(sorted_collection) and sorted_collection[bound] == item: + return bound + last_bound = bound / 2 + sorted_collection = sorted_collection[int(last_bound) : bound + 1] + index_in_optimized_list = binary_search( + sorted_collection=sorted_collection, item=item + ) + if index_in_optimized_list is None: + return None + return int(index_in_optimized_list) + int(bound / 2) From dbbe1358044fff9acf7c56a2c829d25f25250ca1 Mon Sep 17 00:00:00 2001 From: kiarash8112 Date: Fri, 20 Oct 2023 14:47:42 -0400 Subject: [PATCH 02/18] replace binary_search with binary_search_recursion --- searches/exponential_search.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/searches/exponential_search.py b/searches/exponential_search.py index 36c32f4509fb..a1de4c8c6831 100644 --- a/searches/exponential_search.py +++ b/searches/exponential_search.py @@ -1,17 +1,12 @@ -from binary_search import binary_search +from binary_search import binary_search_by_recursion def exponential_search(sorted_collection: list[int], item: int) -> int | None: bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 - if bound < len(sorted_collection) and sorted_collection[bound] == item: - return bound - last_bound = bound / 2 - sorted_collection = sorted_collection[int(last_bound) : bound + 1] - index_in_optimized_list = binary_search( - sorted_collection=sorted_collection, item=item + left = bound / 2 + right = min(bound, len(sorted_collection) - 1) + return binary_search_by_recursion( + sorted_collection=sorted_collection, item=item, left=left, right=right ) - if index_in_optimized_list is None: - return None - return int(index_in_optimized_list) + int(bound / 2) From 1d79bd0d359644ff8aa07f8d07304b0c2f834ba9 Mon Sep 17 00:00:00 2001 From: kiarash8112 Date: Fri, 20 Oct 2023 14:53:37 -0400 Subject: [PATCH 03/18] convert left type to int to be useable in binary_search_recursion --- searches/exponential_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searches/exponential_search.py b/searches/exponential_search.py index a1de4c8c6831..3b028ff96706 100644 --- a/searches/exponential_search.py +++ b/searches/exponential_search.py @@ -5,7 +5,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int | None: bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 - left = bound / 2 + left = int(bound / 2) right = min(bound, len(sorted_collection) - 1) return binary_search_by_recursion( sorted_collection=sorted_collection, item=item, left=left, right=right From ff30ae0c21dc8d531633b17eefb314d550d31c59 Mon Sep 17 00:00:00 2001 From: kiarash8112 Date: Fri, 20 Oct 2023 14:57:20 -0400 Subject: [PATCH 04/18] add docs and tests for exponential_search algorithm --- searches/exponential_search.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/searches/exponential_search.py b/searches/exponential_search.py index 3b028ff96706..233763a32731 100644 --- a/searches/exponential_search.py +++ b/searches/exponential_search.py @@ -1,7 +1,38 @@ +""" +This is pure Python implementation of Exponential search. + +Resources used: +https://en.wikipedia.org/wiki/Exponential_search + +""" from binary_search import binary_search_by_recursion def exponential_search(sorted_collection: list[int], item: int) -> int | None: + """Pure implementation of exponential search algorithm in Python + + 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 found item or None if 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) + + """ bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 @@ -10,3 +41,8 @@ def exponential_search(sorted_collection: list[int], item: int) -> int | None: return binary_search_by_recursion( sorted_collection=sorted_collection, item=item, left=left, right=right ) + +if __name__ == "__main__": + import doctest + + doctest.testmod() From f66e566b5087d2eb5a54f0faf6212deffb00aa76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:02:19 +0000 Subject: [PATCH 05/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- searches/exponential_search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/searches/exponential_search.py b/searches/exponential_search.py index 233763a32731..66e4d498ee30 100644 --- a/searches/exponential_search.py +++ b/searches/exponential_search.py @@ -42,6 +42,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int | None: sorted_collection=sorted_collection, item=item, left=left, right=right ) + if __name__ == "__main__": import doctest From 16f58e17aa20d97b7f6a6c66f5b773c93c0c6daa Mon Sep 17 00:00:00 2001 From: kiarash8112 Date: Fri, 20 Oct 2023 15:19:10 -0400 Subject: [PATCH 06/18] move exponential_search to binary_search.py to pass github auto build tests delete exponential_search.py file --- searches/binary_search.py | 43 +++++++++++++++++++++++++++++ searches/exponential_search.py | 49 ---------------------------------- 2 files changed, 43 insertions(+), 49 deletions(-) delete mode 100644 searches/exponential_search.py diff --git a/searches/binary_search.py b/searches/binary_search.py index 05dadd4fe965..bf8198767b07 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -297,6 +297,49 @@ 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 | None: + """Pure implementation of 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 found item or None if 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) + + """ + bound = 1 + while bound < len(sorted_collection) and sorted_collection[bound] < item: + bound *= 2 + left = int(bound / 2) + right = min(bound, len(sorted_collection) - 1) + return binary_search_by_recursion( + sorted_collection=sorted_collection, item=item, left=left, right=right + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + if __name__ == "__main__": user_input = input("Enter numbers separated by comma:\n").strip() collection = sorted(int(item) for item in user_input.split(",")) diff --git a/searches/exponential_search.py b/searches/exponential_search.py deleted file mode 100644 index 66e4d498ee30..000000000000 --- a/searches/exponential_search.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -This is pure Python implementation of Exponential search. - -Resources used: -https://en.wikipedia.org/wiki/Exponential_search - -""" -from binary_search import binary_search_by_recursion - - -def exponential_search(sorted_collection: list[int], item: int) -> int | None: - """Pure implementation of exponential search algorithm in Python - - 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 found item or None if 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) - - """ - bound = 1 - while bound < len(sorted_collection) and sorted_collection[bound] < item: - bound *= 2 - left = int(bound / 2) - right = min(bound, len(sorted_collection) - 1) - return binary_search_by_recursion( - sorted_collection=sorted_collection, item=item, left=left, right=right - ) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 26b5283d35f026e8a4a7b6525962d114cfa4322a Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 02:46:35 -0400 Subject: [PATCH 07/18] Update searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/searches/binary_search.py b/searches/binary_search.py index bf8198767b07..0eb132494389 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -324,6 +324,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int | None: >>> exponential_search([0, 5, 7, 10, 15], 6) """ + sorted_collection = sorted(sorted_collection) bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 From 63bf03a230c0ce2c4aa10ba9468d2ee891add8b3 Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 02:47:10 -0400 Subject: [PATCH 08/18] remove additional space searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 0eb132494389..a13375b798a8 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -314,15 +314,12 @@ def exponential_search(sorted_collection: list[int], item: int) -> int | None: 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) - + >>> exponential_search([0, 5, 7, 10, 15], 6) = None + True """ sorted_collection = sorted(sorted_collection) bound = 1 From 4ca8a6ab43886f8723b8607f7b592b33374123a7 Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 02:47:37 -0400 Subject: [PATCH 09/18] return single data type in exponential_search searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index a13375b798a8..9921cf8b5dcc 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -297,7 +297,7 @@ 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 | None: +def exponential_search(sorted_collection: list[int], item: int) -> int: """Pure implementation of exponential search algorithm in Python Resources used: https://en.wikipedia.org/wiki/Exponential_search From b5244049f1e7db7b5ea267d3b3ed6324116398c8 Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 02:48:43 -0400 Subject: [PATCH 10/18] add doctest mod searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 9921cf8b5dcc..94783c2d28ed 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -338,7 +338,6 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: doctest.testmod() -if __name__ == "__main__": user_input = input("Enter numbers separated by comma:\n").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")) From dc9155a8fff192fdb0212bd63fa273b1098cbc60 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 06:49:10 +0000 Subject: [PATCH 11/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- searches/binary_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 94783c2d28ed..1be099a38e40 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -337,7 +337,6 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: doctest.testmod() - user_input = input("Enter numbers separated by comma:\n").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")) From afbd45858b09350e8bc934c225c6bfbf8ed0abbf Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 03:40:49 -0400 Subject: [PATCH 12/18] use // instread of int() convert searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 1be099a38e40..c54753857e3d 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -325,7 +325,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 - left = int(bound / 2) + left = bound // 2 right = min(bound, len(sorted_collection) - 1) return binary_search_by_recursion( sorted_collection=sorted_collection, item=item, left=left, right=right From d8dfe74532efdc656c38199f24cd7aba9e41f7f7 Mon Sep 17 00:00:00 2001 From: Kiarash Hajian <133909368+kiarash8112@users.noreply.github.com> Date: Sat, 21 Oct 2023 03:41:33 -0400 Subject: [PATCH 13/18] change test according to new code searches/binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index c54753857e3d..313d4f9f2e97 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -318,8 +318,8 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: 4 >>> exponential_search([0, 5, 7, 10, 15], 5) 1 - >>> exponential_search([0, 5, 7, 10, 15], 6) = None - True + >>> exponential_search([0, 5, 7, 10, 15], 6) + -1 """ sorted_collection = sorted(sorted_collection) bound = 1 From 66a03cba47cc2331cf9d9f3a97b79d88f5850b71 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 21 Oct 2023 03:49:09 -0400 Subject: [PATCH 14/18] fix binary_search_recursion multiple type return error --- searches/binary_search.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 313d4f9f2e97..c58cd1ebca88 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -327,9 +327,12 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: bound *= 2 left = bound // 2 right = min(bound, len(sorted_collection) - 1) - return binary_search_by_recursion( + 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 if __name__ == "__main__": From d76119bfe2af1acfae2ed9f7abe29fca6bc6be7c Mon Sep 17 00:00:00 2001 From: user Date: Sat, 21 Oct 2023 03:55:18 -0400 Subject: [PATCH 15/18] add a timeit benchmark for exponential_search --- searches/binary_search.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index c58cd1ebca88..4a50d9065958 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -321,7 +321,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: >>> exponential_search([0, 5, 7, 10, 15], 6) -1 """ - sorted_collection = sorted(sorted_collection) + # sorted_collection = sorted(sorted_collection) bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 @@ -337,9 +337,24 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: if __name__ == "__main__": import doctest + import timeit doctest.testmod() + random_list = list(range(1000)) + exponential_search_result = timeit.timeit( + stmt="exponential_search(random_list, 0)", + setup="from __main__ import exponential_search,random_list", + number=10000, + ) + binary_search_result = timeit.timeit( + stmt="binary_search(random_list, 0)", + setup="from __main__ import binary_search,random_list", + number=10000, + ) + print("exponential_search", exponential_search_result) + print("binary_search", binary_search_result) + user_input = input("Enter numbers separated by comma:\n").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")) From dc32f8697903b02dcc742dc13d492375697c2c84 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 21 Oct 2023 03:59:28 -0400 Subject: [PATCH 16/18] sort input of binary search to be equal in performance test with exponential_search --- searches/binary_search.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 4a50d9065958..8ff587031b12 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -214,6 +214,8 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None: >>> binary_search([0, 5, 7, 10, 15], 6) """ + sorted_collection = sorted(sorted_collection) + left = 0 right = len(sorted_collection) - 1 @@ -321,7 +323,7 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: >>> exponential_search([0, 5, 7, 10, 15], 6) -1 """ - # sorted_collection = sorted(sorted_collection) + sorted_collection = sorted(sorted_collection) bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 From 0b53cdd0d754c6e4274e15851c8145a33dde40df Mon Sep 17 00:00:00 2001 From: user Date: Sat, 21 Oct 2023 05:14:50 -0400 Subject: [PATCH 17/18] raise value error instead of sorting input in binary and exonential search to fix bugs --- searches/binary_search.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index 8ff587031b12..ee03538667f3 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -214,8 +214,11 @@ def binary_search(sorted_collection: list[int], item: int) -> int | None: >>> binary_search([0, 5, 7, 10, 15], 6) """ - sorted_collection = sorted(sorted_collection) - + if not all( + sorted_collection[i] <= sorted_collection[i + 1] + for i in range(len(sorted_collection) - 1) + ): + raise ValueError("input of function is not sorted") left = 0 right = len(sorted_collection) - 1 @@ -323,7 +326,11 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: >>> exponential_search([0, 5, 7, 10, 15], 6) -1 """ - sorted_collection = sorted(sorted_collection) + if not all( + sorted_collection[i] <= sorted_collection[i + 1] + for i in range(len(sorted_collection) - 1) + ): + raise ValueError("input of function is not sorted") bound = 1 while bound < len(sorted_collection) and sorted_collection[bound] < item: bound *= 2 From 49744fb730a718a9764cc29b96c38d32ee8fbbe4 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 21 Oct 2023 20:39:09 +0200 Subject: [PATCH 18/18] Update binary_search.py --- searches/binary_search.py | 139 +++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 77 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index ee03538667f3..586be39c9a0d 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -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,34 +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 not all( - sorted_collection[i] <= sorted_collection[i + 1] - for i in range(len(sorted_collection) - 1) - ): - raise ValueError("input of function is not sorted") + if list(sorted_collection) != sorted(sorted_collection): + raise ValueError("sorted_collection must be sorted in ascending order") left = 0 right = len(sorted_collection) - 1 @@ -231,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 @@ -303,16 +283,16 @@ def binary_search_by_recursion( def exponential_search(sorted_collection: list[int], item: int) -> int: - """Pure implementation of exponential search algorithm in Python + """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 + 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 found item or None if item is not found + :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 @@ -326,11 +306,8 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: >>> exponential_search([0, 5, 7, 10, 15], 6) -1 """ - if not all( - sorted_collection[i] <= sorted_collection[i + 1] - for i in range(len(sorted_collection) - 1) - ): - raise ValueError("input of function is not sorted") + 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 @@ -344,31 +321,39 @@ def exponential_search(sorted_collection: list[int], item: int) -> int: return last_result +searches = ( # Fastest to slowest... + binary_search_std_lib, + binary_search, + exponential_search, + binary_search_by_recursion, +) + + if __name__ == "__main__": import doctest import timeit doctest.testmod() - - random_list = list(range(1000)) - exponential_search_result = timeit.timeit( - stmt="exponential_search(random_list, 0)", - setup="from __main__ import exponential_search,random_list", - number=10000, - ) - binary_search_result = timeit.timeit( - stmt="binary_search(random_list, 0)", - setup="from __main__ import binary_search,random_list", - number=10000, - ) - print("exponential_search", exponential_search_result) - print("binary_search", binary_search_result) - - user_input = input("Enter numbers separated by comma:\n").strip() + 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}.")