diff --git a/searches/ternary_search.py b/searches/ternary_search.py index 73e4b1ddc68b..94da414089a2 100644 --- a/searches/ternary_search.py +++ b/searches/ternary_search.py @@ -1,158 +1,111 @@ """ -This is a type of divide and conquer algorithm which divides the search space into -3 parts and finds the target value based on the property of the array or list -(usually monotonic property). +Ternary Search Algorithm - Iterative and Recursive Implementations + +Divides the search range into 3 parts instead of 2, and recursively or iteratively +eliminates 2/3 of the array in each step. Time Complexity : O(log3 N) -Space Complexity : O(1) +Space Complexity : O(1) for iterative, O(log3 N) for recursive """ from __future__ import annotations -# This is the precision for this function which can be altered. -# It is recommended for users to keep this number greater than or equal to 10. -precision = 10 - - -# This is the linear search that will occur after the search space has become smaller. - - -def lin_search(left: int, right: int, array: list[int], target: int) -> int: - """Perform linear search in list. Returns -1 if element is not found. - - Parameters - ---------- - left : int - left index bound. - right : int - right index bound. - array : List[int] - List of elements to be searched on - target : int - Element that is searched - Returns - ------- - int - index of element that is looked for. +def linear_search(start: int, end: int, array: list[int | float | str], target) -> int: + """ + Fallback linear search when search window is small. - Examples - -------- - >>> lin_search(0, 4, [4, 5, 6, 7], 7) - 3 - >>> lin_search(0, 3, [4, 5, 6, 7], 7) - -1 - >>> lin_search(0, 2, [-18, 2], -18) - 0 - >>> lin_search(0, 1, [5], 5) - 0 - >>> lin_search(0, 3, ['a', 'c', 'd'], 'c') - 1 - >>> lin_search(0, 3, [.1, .4 , -.1], .1) - 0 - >>> lin_search(0, 3, [.1, .4 , -.1], -.1) + >>> linear_search(0, 4, [1, 2, 3, 4], 3) 2 + >>> linear_search(0, 2, ["a", "b", "c"], "b") + 1 + >>> linear_search(0, 3, [0.1, 0.2, 0.3], 0.4) + -1 """ - for i in range(left, right): + for i in range(start, end): if array[i] == target: return i return -1 -def ite_ternary_search(array: list[int], target: int) -> int: - """Iterative method of the ternary search algorithm. - >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] - >>> ite_ternary_search(test_list, 3) - -1 - >>> ite_ternary_search(test_list, 13) - 4 - >>> ite_ternary_search([4, 5, 6, 7], 4) - 0 - >>> ite_ternary_search([4, 5, 6, 7], -10) - -1 - >>> ite_ternary_search([-18, 2], -18) - 0 - >>> ite_ternary_search([5], 5) - 0 - >>> ite_ternary_search(['a', 'c', 'd'], 'c') - 1 - >>> ite_ternary_search(['a', 'c', 'd'], 'f') +def ternary_search_iterative(array: list[int | float | str], target) -> int: + """ + Iterative ternary search algorithm for sorted arrays. + + >>> ternary_search_iterative([1, 3, 5, 7, 9], 7) + 3 + >>> ternary_search_iterative([1, 3, 5, 7, 9], 2) -1 - >>> ite_ternary_search([], 1) + >>> ternary_search_iterative([], 4) -1 - >>> ite_ternary_search([.1, .4 , -.1], .1) - 0 """ - left = 0 - right = len(array) + right = len(array) - 1 + threshold = 10 + while left <= right: - if right - left < precision: - return lin_search(left, right, array, target) + if right - left < threshold: + return linear_search(left, right + 1, array, target) - one_third = (left + right) // 3 + 1 - two_third = 2 * (left + right) // 3 + 1 + one_third = left + (right - left) // 3 + two_third = right - (right - left) // 3 if array[one_third] == target: return one_third elif array[two_third] == target: return two_third - elif target < array[one_third]: right = one_third - 1 - elif array[two_third] < target: + elif target > array[two_third]: left = two_third + 1 - else: left = one_third + 1 right = two_third - 1 + return -1 -def rec_ternary_search(left: int, right: int, array: list[int], target: int) -> int: - """Recursive method of the ternary search algorithm. +def ternary_search_recursive( + array: list[int | float | str], + target, + left: int = 0, + right: int | None = None, + threshold: int = 10, +) -> int: + """ + Recursive ternary search algorithm. - >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] - >>> rec_ternary_search(0, len(test_list), test_list, 3) - -1 - >>> rec_ternary_search(4, len(test_list), test_list, 42) - 8 - >>> rec_ternary_search(0, 2, [4, 5, 6, 7], 4) - 0 - >>> rec_ternary_search(0, 3, [4, 5, 6, 7], -10) - -1 - >>> rec_ternary_search(0, 1, [-18, 2], -18) - 0 - >>> rec_ternary_search(0, 1, [5], 5) - 0 - >>> rec_ternary_search(0, 2, ['a', 'c', 'd'], 'c') - 1 - >>> rec_ternary_search(0, 2, ['a', 'c', 'd'], 'f') - -1 - >>> rec_ternary_search(0, 0, [], 1) + >>> ternary_search_recursive([1, 3, 5, 7, 9], 7) + 3 + >>> ternary_search_recursive(["a", "b", "c", "d"], "c") + 2 + >>> ternary_search_recursive([], 1) -1 - >>> rec_ternary_search(0, 3, [.1, .4 , -.1], .1) - 0 """ - if left < right: - if right - left < precision: - return lin_search(left, right, array, target) - one_third = (left + right) // 3 + 1 - two_third = 2 * (left + right) // 3 + 1 + if right is None: + right = len(array) - 1 - if array[one_third] == target: - return one_third - elif array[two_third] == target: - return two_third + if left > right: + return -1 - elif target < array[one_third]: - return rec_ternary_search(left, one_third - 1, array, target) - elif array[two_third] < target: - return rec_ternary_search(two_third + 1, right, array, target) - else: - return rec_ternary_search(one_third + 1, two_third - 1, array, target) + if right - left < threshold: + return linear_search(left, right + 1, array, target) + + one_third = left + (right - left) // 3 + two_third = right - (right - left) // 3 + + if array[one_third] == target: + return one_third + elif array[two_third] == target: + return two_third + elif target < array[one_third]: + return ternary_search_recursive(array, target, left, one_third - 1, threshold) + elif target > array[two_third]: + return ternary_search_recursive(array, target, two_third + 1, right, threshold) else: - return -1 + return ternary_search_recursive( + array, target, one_third + 1, two_third - 1, threshold + ) if __name__ == "__main__": @@ -160,14 +113,21 @@ def rec_ternary_search(left: int, right: int, array: list[int], target: int) -> doctest.testmod() - user_input = input("Enter numbers separated by comma:\n").strip() - collection = [int(item.strip()) for item in user_input.split(",")] - assert collection == sorted(collection), f"List must be ordered.\n{collection}." - target = int(input("Enter the number to be found in the list:\n").strip()) - result1 = ite_ternary_search(collection, target) - result2 = rec_ternary_search(0, len(collection) - 1, collection, target) - if result2 != -1: - print(f"Iterative search: {target} found at positions: {result1}") - print(f"Recursive search: {target} found at positions: {result2}") - else: - print("Not found") + try: + raw_input = input("\nEnter sorted numbers separated by commas: ").strip() + collection = [int(x) for x in raw_input.split(",")] + assert collection == sorted(collection), ( + "Input must be sorted in ascending order." + ) + target = int(input("Enter the number to search: ")) + + result_iter = ternary_search_iterative(collection, target) + result_rec = ternary_search_recursive(collection, target) + + if result_iter != -1: + print(f"Iterative Search: Found {target} at index {result_iter}") + print(f"Recursive Search: Found {target} at index {result_rec}") + else: + print(f"{target} not found in the list.") + except Exception as e: + print(f"Error: {e}")