Skip to content

Add refined ternary search algorithm (iterative and recursive) #12847

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 84 additions & 124 deletions searches/ternary_search.py
Original file line number Diff line number Diff line change
@@ -1,173 +1,133 @@
"""
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__":
import doctest

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:

Check failure on line 132 in searches/ternary_search.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (BLE001)

searches/ternary_search.py:132:12: BLE001 Do not catch blind exception: `Exception`
print(f"Error: {e}")
Loading