diff --git a/searches/binary_search.py b/searches/binary_search.py index 35e0dd0596d2..b2912c4464ee 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -12,9 +12,11 @@ import bisect from typing import List, Optional +from searches.comparable_type import ComparableType + def bisect_left( - sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 + sorted_collection: List[ComparableType], item: ComparableType, lo: int = 0, hi: int = -1 ) -> int: """ Locates the first element in a sorted array that is larger or equal to a given @@ -60,7 +62,7 @@ def bisect_left( def bisect_right( - sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 + sorted_collection: List[ComparableType], item: ComparableType, lo: int = 0, hi: int = -1 ) -> int: """ Locates the first element in a sorted array that is larger than a given value. @@ -105,7 +107,7 @@ def bisect_right( def insort_left( - sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 + sorted_collection: List[ComparableType], item: ComparableType, lo: int = 0, hi: int = -1 ) -> None: """ Inserts a given value into a sorted array before other values with the same value. @@ -148,7 +150,7 @@ def insort_left( def insort_right( - sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 + sorted_collection: List[ComparableType], item: ComparableType, lo: int = 0, hi: int = -1 ) -> None: """ Inserts a given value into a sorted array after other values with the same value. @@ -190,7 +192,7 @@ def insort_right( sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) -def binary_search(sorted_collection: List[int], item: int) -> Optional[int]: +def binary_search(sorted_collection: List[ComparableType], item: ComparableType) -> Optional[int]: """Pure implementation of binary search algorithm in Python Be careful collection must be ascending sorted, otherwise result will be @@ -228,7 +230,7 @@ def binary_search(sorted_collection: List[int], item: int) -> Optional[int]: return None -def binary_search_std_lib(sorted_collection: List[int], item: int) -> Optional[int]: +def binary_search_std_lib(sorted_collection: List[ComparableType], item: ComparableType) -> Optional[int]: """Pure implementation of binary search algorithm in Python using stdlib Be careful collection must be ascending sorted, otherwise result will be @@ -258,7 +260,7 @@ def binary_search_std_lib(sorted_collection: List[int], item: int) -> Optional[i def binary_search_by_recursion( - sorted_collection: List[int], item: int, left: int, right: int + sorted_collection: List[ComparableType], item: ComparableType, left: int, right: int ) -> Optional[int]: """Pure implementation of binary search algorithm in Python by recursion diff --git a/searches/binary_tree_traversal.py b/searches/binary_tree_traversal.py index f919a2962354..365d936231f0 100644 --- a/searches/binary_tree_traversal.py +++ b/searches/binary_tree_traversal.py @@ -260,7 +260,7 @@ def post_order_iter(node: TreeNode) -> None: print(stack2.pop().data, end=",") -def prompt(s: str = "", width=50, char="*") -> str: +def prompt(s: str = "", width: int = 50, char: str = "*") -> str: if not s: return "\n" + width * char left, extra = divmod(width - len(s) - 2, 2) diff --git a/searches/comparable_type.py b/searches/comparable_type.py new file mode 100644 index 000000000000..a90abf88157a --- /dev/null +++ b/searches/comparable_type.py @@ -0,0 +1,28 @@ +from abc import abstractmethod +from typing import Protocol, TypeVar + +ComparableType = TypeVar('ComparableType', bound='Comparable') + + +class Comparable(Protocol): + @abstractmethod + def __lt__(self: ComparableType, other: ComparableType) -> bool: + pass + + @abstractmethod + def __le__(self: ComparableType, other: ComparableType) -> bool: + pass + + @abstractmethod + def __gt__(self: ComparableType, other: ComparableType) -> bool: + pass + + @abstractmethod + def __ge__(self: ComparableType, other: ComparableType) -> bool: + pass + + @abstractmethod + def __eq__(self: ComparableType, other: object) -> bool: + if not isinstance(other, type(self)): + return False + raise NotImplemented diff --git a/searches/double_linear_search.py b/searches/double_linear_search.py index d9dad3c685b6..1c53e3a3923e 100644 --- a/searches/double_linear_search.py +++ b/searches/double_linear_search.py @@ -1,7 +1,11 @@ from __future__ import annotations +from typing import TypeVar -def double_linear_search(array: list[int], search_item: int) -> int: +T = TypeVar('T') + + +def double_linear_search(array: list[T], search_item: T) -> int: """ Iterate through the array from both sides to find the index of search_item. diff --git a/searches/double_linear_search_recursion.py b/searches/double_linear_search_recursion.py index 1c483e974ca7..680a092ba66a 100644 --- a/searches/double_linear_search_recursion.py +++ b/searches/double_linear_search_recursion.py @@ -1,4 +1,9 @@ -def search(list_data: list, key: int, left: int = 0, right: int = 0) -> int: +from typing import TypeVar + +T = TypeVar('T') + + +def search(list_data: list[T], key: T, left: int = 0, right: int = 0) -> int: """ Iterate through the array to find the index of key using recursion. :param list_data: the list to be searched diff --git a/searches/fibonacci_search.py b/searches/fibonacci_search.py index ac8ecc99a187..421f43559de1 100644 --- a/searches/fibonacci_search.py +++ b/searches/fibonacci_search.py @@ -11,6 +11,9 @@ python3 fibonacci_search.py """ from functools import lru_cache +from typing import TypeVar + +from searches.comparable_type import Comparable, ComparableType @lru_cache() @@ -54,7 +57,7 @@ def fibonacci(k: int) -> int: return fibonacci(k - 1) + fibonacci(k - 2) -def fibonacci_search(arr: list, val: int) -> int: +def fibonacci_search(arr: list[ComparableType], val: ComparableType) -> int: """A pure Python implementation of a fibonacci search algorithm. Parameters diff --git a/searches/hill_climbing.py b/searches/hill_climbing.py index bb24e781a6c1..996598e64ea7 100644 --- a/searches/hill_climbing.py +++ b/searches/hill_climbing.py @@ -84,7 +84,7 @@ def __str__(self): def hill_climbing( - search_prob, + search_prob: SearchProblem, find_max: bool = True, max_x: float = math.inf, min_x: float = -math.inf, diff --git a/searches/interpolation_search.py b/searches/interpolation_search.py index f4fa8e1203df..552fe82a802d 100644 --- a/searches/interpolation_search.py +++ b/searches/interpolation_search.py @@ -1,9 +1,12 @@ """ This is pure Python implementation of interpolation search algorithm """ +from typing import Optional +from searches.comparable_type import ComparableType -def interpolation_search(sorted_collection, item): + +def interpolation_search(sorted_collection: list[int], item: int) -> Optional[int]: """Pure implementation of interpolation search algorithm in Python Be careful collection must be ascending sorted, otherwise result will be unpredictable @@ -23,7 +26,7 @@ def interpolation_search(sorted_collection, item): return None point = left + ((item - sorted_collection[left]) * (right - left)) // ( - sorted_collection[right] - sorted_collection[left] + sorted_collection[right] - sorted_collection[left] ) # out of range check @@ -48,8 +51,10 @@ def interpolation_search(sorted_collection, item): return None -def interpolation_search_by_recursion(sorted_collection, item, left, right): - +def interpolation_search_by_recursion(sorted_collection: list[int], + item: int, + left: int, + right: int) -> Optional[int]: """Pure implementation of interpolation search algorithm in Python by recursion Be careful collection must be ascending sorted, otherwise result will be unpredictable @@ -67,7 +72,7 @@ def interpolation_search_by_recursion(sorted_collection, item, left, right): return None point = left + ((item - sorted_collection[left]) * (right - left)) // ( - sorted_collection[right] - sorted_collection[left] + sorted_collection[right] - sorted_collection[left] ) # out of range check @@ -91,7 +96,7 @@ def interpolation_search_by_recursion(sorted_collection, item, left, right): ) -def __assert_sorted(collection): +def __assert_sorted(collection: list[ComparableType]): """Check if collection is ascending sorted, if not - raises :py:class:`ValueError` :param collection: collection :return: True if collection is ascending sorted diff --git a/searches/jump_search.py b/searches/jump_search.py index 31a9656c55fe..a8d980555cfd 100644 --- a/searches/jump_search.py +++ b/searches/jump_search.py @@ -8,8 +8,10 @@ import math +from searches.comparable_type import ComparableType -def jump_search(arr: list, x: int) -> int: + +def jump_search(arr: list[ComparableType], x: ComparableType) -> int: """ Pure Python implementation of the jump search algorithm. Examples: diff --git a/searches/linear_search.py b/searches/linear_search.py index 777080d14e36..091f35b9aa71 100644 --- a/searches/linear_search.py +++ b/searches/linear_search.py @@ -7,9 +7,12 @@ For manual testing run: python3 linear_search.py """ +from typing import TypeVar +T = TypeVar('T') -def linear_search(sequence: list, target: int) -> int: + +def linear_search(sequence: list[T], target: T) -> int: """A pure Python implementation of a linear search algorithm :param sequence: a collection with comparable items (as sorted items not required @@ -33,7 +36,7 @@ def linear_search(sequence: list, target: int) -> int: return -1 -def rec_linear_search(sequence: list, low: int, high: int, target: int) -> int: +def rec_linear_search(sequence: list[T], low: int, high: int, target: T) -> int: """ A pure Python implementation of a recursive linear search algorithm diff --git a/searches/quick_select.py b/searches/quick_select.py index 5ede8c4dd07f..8325be3029ac 100644 --- a/searches/quick_select.py +++ b/searches/quick_select.py @@ -5,9 +5,15 @@ https://en.wikipedia.org/wiki/Quickselect """ import random +from typing import Optional +from searches.comparable_type import ComparableType -def _partition(data: list, pivot) -> tuple: + +def _partition( + data: list[ComparableType], + pivot: ComparableType +) -> tuple[list[ComparableType], list[ComparableType], list[ComparableType]]: """ Three way partition the data into smaller, equal and greater lists, in relationship to the pivot @@ -26,7 +32,7 @@ def _partition(data: list, pivot) -> tuple: return less, equal, greater -def quick_select(items: list, index: int): +def quick_select(items: list[ComparableType], index: int) -> Optional[ComparableType]: """ >>> quick_select([2, 4, 5, 7, 899, 54, 32], 5) 54 diff --git a/searches/sentinel_linear_search.py b/searches/sentinel_linear_search.py index 69c1cf9f351a..4c203d45e293 100644 --- a/searches/sentinel_linear_search.py +++ b/searches/sentinel_linear_search.py @@ -9,9 +9,12 @@ For manual testing run: python sentinel_linear_search.py """ +from typing import Optional +from searches.comparable_type import ComparableType -def sentinel_linear_search(sequence, target): + +def sentinel_linear_search(sequence: list[ComparableType], target: ComparableType) -> Optional[int]: """Pure implementation of sentinel linear search algorithm in Python :param sequence: some sequence with comparable items diff --git a/searches/simple_binary_search.py b/searches/simple_binary_search.py index d1f7f7a51cbc..9c3cb49223b4 100644 --- a/searches/simple_binary_search.py +++ b/searches/simple_binary_search.py @@ -9,8 +9,10 @@ """ from __future__ import annotations +from searches.comparable_type import ComparableType -def binary_search(a_list: list[int], item: int) -> bool: + +def binary_search(a_list: list[ComparableType], item: ComparableType) -> bool: """ >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] >>> print(binary_search(test_list, 3)) diff --git a/searches/simulated_annealing.py b/searches/simulated_annealing.py index 2aa980be7748..f761a768c33e 100644 --- a/searches/simulated_annealing.py +++ b/searches/simulated_annealing.py @@ -6,16 +6,16 @@ def simulated_annealing( - search_prob, - find_max: bool = True, - max_x: float = math.inf, - min_x: float = -math.inf, - max_y: float = math.inf, - min_y: float = -math.inf, - visualization: bool = False, - start_temperate: float = 100, - rate_of_decrease: float = 0.01, - threshold_temp: float = 1, + search_prob: SearchProblem, + find_max: bool = True, + max_x: float = math.inf, + min_x: float = -math.inf, + max_y: float = math.inf, + min_y: float = -math.inf, + visualization: bool = False, + start_temperate: float = 100, + rate_of_decrease: float = 0.01, + threshold_temp: float = 1, ) -> SearchProblem: """ Implementation of the simulated annealing algorithm. We start with a given state, @@ -51,17 +51,17 @@ def simulated_annealing( next_state = None neighbors = current_state.get_neighbors() while ( - next_state is None and neighbors + next_state is None and neighbors ): # till we do not find a neighbor that we can move to index = random.randint(0, len(neighbors) - 1) # picking a random neighbor picked_neighbor = neighbors.pop(index) change = picked_neighbor.score() - current_score if ( - picked_neighbor.x > max_x - or picked_neighbor.x < min_x - or picked_neighbor.y > max_y - or picked_neighbor.y < min_y + picked_neighbor.x > max_x + or picked_neighbor.x < min_x + or picked_neighbor.y > max_y + or picked_neighbor.y < min_y ): continue # neighbor outside our bounds @@ -71,7 +71,7 @@ def simulated_annealing( next_state = picked_neighbor else: probability = (math.e) ** ( - change / current_temp + change / current_temp ) # probability generation function if random.random() < probability: # random number within probability next_state = picked_neighbor @@ -90,14 +90,16 @@ def simulated_annealing( plt.xlabel("Iterations") plt.ylabel("Function values") plt.show() + + assert best_state is not None # This is a mypy issue, it does not recognize the none being set at the start of the loop return best_state if __name__ == "__main__": - def test_f1(x, y): return (x ** 2) + (y ** 2) + # starting the problem with initial coordinates (12, 47) prob = SearchProblem(x=12, y=47, step_size=1, function_to_optimize=test_f1) local_min = simulated_annealing( @@ -118,9 +120,11 @@ def test_f1(x, y): f"and 50 > y > - 5 found via hill climbing: {local_min.score()}" ) + def test_f2(x, y): return (3 * x ** 2) - (6 * y) + prob = SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_f1) local_min = simulated_annealing(prob, find_max=False, visualization=True) print( diff --git a/searches/ternary_search.py b/searches/ternary_search.py index 9422a4ccb966..7daeda6e43b0 100644 --- a/searches/ternary_search.py +++ b/searches/ternary_search.py @@ -8,15 +8,18 @@ """ from typing import List +from searches.comparable_type import ComparableType + # 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: +def lin_search(left: int, right: int, array: List[ComparableType], target: ComparableType) -> int: """Perform linear search in list. Returns -1 if element is not found. Parameters @@ -58,7 +61,7 @@ def lin_search(left: int, right: int, array: List[int], target: int) -> int: return -1 -def ite_ternary_search(array: List[int], target: int) -> int: +def ite_ternary_search(array: List[ComparableType], target: ComparableType) -> 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) @@ -89,8 +92,8 @@ def ite_ternary_search(array: List[int], target: int) -> int: if right - left < precision: return lin_search(left, right, array, target) - one_third = (left + right) / 3 + 1 - two_third = 2 * (left + right) / 3 + 1 + one_third = (left + right) // 3 + 1 + two_third = 2 * (left + right) // 3 + 1 if array[one_third] == target: return one_third @@ -110,7 +113,7 @@ def ite_ternary_search(array: List[int], target: int) -> int: return -1 -def rec_ternary_search(left: int, right: int, array: List[int], target: int) -> int: +def rec_ternary_search(left: int, right: int, array: List[ComparableType], target: ComparableType) -> int: """Recursive method of the ternary search algorithm. >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] @@ -138,8 +141,8 @@ def rec_ternary_search(left: int, right: int, array: List[int], target: int) -> 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 + one_third = (left + right) // 3 + 1 + two_third = 2 * (left + right) // 3 + 1 if array[one_third] == target: return one_third