Skip to content

Add type hints and tests. #2461

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

Merged
merged 13 commits into from
Nov 14, 2020
166 changes: 115 additions & 51 deletions searches/ternary_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,131 @@
Time Complexity : O(log3 N)
Space Complexity : O(1)
"""
import sys
from typing import List

# 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, right, A, target):
for i in range(left, right + 1):
def lin_search(left: int, right: int, A: 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.
A : List[int]
List of elements to be searched on
target : int
Element that is searched

Returns
-------
int
index of element that is looked for.

Examples
--------
>>> print(lin_search(0, 4, [4, 5, 6, 7], 7))
3
>>> print(lin_search(0, 3, [4, 5, 6, 7], 7))
-1
>>> print(lin_search(0, 2, [-18, 2], -18))
0
>>> print(lin_search(0, 1, [5], 5))
0
>>> print(lin_search(0, 3, ['a', 'c', 'd'], 'c'))
1
>>> print(lin_search(0, 3, [.1, .4 , -.1], .1))
0
>>> print(lin_search(0, 3, [.1, .4 , -.1], -.1))
2
"""
for i in range(left, right):
if A[i] == target:
return i


# This is the iterative method of the ternary search algorithm.
def ite_ternary_search(A, target):
return -1


def ite_ternary_search(A: List[int], target: int) -> int:
"""Iterative method of the ternary search algorithm.
>>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42]
>>> print(ite_ternary_search(test_list, 3))
-1
>>> print(ite_ternary_search(test_list, 13))
4
>>> print(ite_ternary_search([4, 5, 6, 7], 4))
0
>>> print(ite_ternary_search([4, 5, 6, 7], -10))
-1
>>> print(ite_ternary_search([-18, 2], -18))
0
>>> print(ite_ternary_search([5], 5))
0
>>> print(ite_ternary_search(['a', 'c', 'd'], 'c'))
1
>>> print(ite_ternary_search(['a', 'c', 'd'], 'f'))
-1
>>> print(ite_ternary_search([], 1))
-1
>>> print(ite_ternary_search([.1, .4 , -.1], .1))
0
"""
left = 0
right = len(A) - 1
while True:
if left < right:

if right - left < precision:
return lin_search(left, right, A, target)
right = len(A)
while left <= right:
if right - left < precision:
return lin_search(left, right, A, target)

oneThird = (left + right) / 3 + 1
twoThird = 2 * (left + right) / 3 + 1
oneThird = (left + right) / 3 + 1
twoThird = 2 * (left + right) / 3 + 1

if A[oneThird] == target:
return oneThird
elif A[twoThird] == target:
return twoThird
if A[oneThird] == target:
return oneThird
elif A[twoThird] == target:
return twoThird

elif target < A[oneThird]:
right = oneThird - 1
elif A[twoThird] < target:
left = twoThird + 1
elif target < A[oneThird]:
right = oneThird - 1
elif A[twoThird] < target:
left = twoThird + 1

else:
left = oneThird + 1
right = twoThird - 1
else:
return None


# This is the recursive method of the ternary search algorithm.
def rec_ternary_search(left, right, A, target):
left = oneThird + 1
right = twoThird - 1
else:
return -1


def rec_ternary_search(left: int, right: int, A: List[int], target: int) -> int:
"""Recursive method of the ternary search algorithm.

>>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42]
>>> print(rec_ternary_search(0, len(test_list), test_list, 3))
-1
>>> print(rec_ternary_search(4, len(test_list), test_list, 42))
8
>>> print(rec_ternary_search(0, 2, [4, 5, 6, 7], 4))
0
>>> print(rec_ternary_search(0, 3, [4, 5, 6, 7], -10))
-1
>>> print(rec_ternary_search(0, 1, [-18, 2], -18))
0
>>> print(rec_ternary_search(0, 1, [5], 5))
0
>>> print(rec_ternary_search(0, 2, ['a', 'c', 'd'], 'c'))
1
>>> print(rec_ternary_search(0, 2, ['a', 'c', 'd'], 'f'))
-1
>>> print(rec_ternary_search(0, 0, [], 1))
-1
>>> print(rec_ternary_search(0, 3, [.1, .4 , -.1], .1))
0
"""
if left < right:

if right - left < precision:
Expand All @@ -69,35 +148,20 @@ def rec_ternary_search(left, right, A, target):
return rec_ternary_search(left, oneThird - 1, A, target)
elif A[twoThird] < target:
return rec_ternary_search(twoThird + 1, right, A, target)

else:
return rec_ternary_search(oneThird + 1, twoThird - 1, A, target)
else:
return None


# This function is to check if the array is sorted.
def __assert_sorted(collection):
if collection != sorted(collection):
raise ValueError("Collection must be sorted")
return True
return -1


if __name__ == "__main__":
user_input = input("Enter numbers separated by coma:\n").strip()
collection = [int(item) for item in user_input.split(",")]

try:
__assert_sorted(collection)
except ValueError:
sys.exit("Sequence must be sorted to apply the ternary search")

target_input = input("Enter a single number to be found in the list:\n")
target = int(target_input)
user_input = input("Enter numbers separated by comma:\n").strip()
collection = sorted(int(item.strip()) for item in user_input.split(","))
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 is not None:
if result2 != -1:
print(f"Iterative search: {target} found at positions: {result1}")
print(f"Recursive search: {target} found at positions: {result2}")
Comment on lines 167 to 168
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this provide the right answer if the user inputs an out-of-order list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not, I think it is better to return to the assert_sorted logic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK but let's do it inline. We don't need a separate function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess my error could be avoided by testing that part of the code, however the use of inputs make it hard to test. Do you know any way that the main could be tested ?

Copy link
Member

@cclauss cclauss Sep 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

def perform_searches(collection: List[int], target: int) -> None:
    # put lines 159, 160, and 162-168 under this function and
    # then you can test `perform_searches()` all day long.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I guess thats the same than testing each one of the two searches functions.
I meant something to try to test different user inputs via the input function, not when the collection is already defined.

else:
Expand Down