From c81cc269962b346420b9e551782bd836dedb4528 Mon Sep 17 00:00:00 2001 From: Isidro Date: Thu, 22 May 2025 22:44:25 +0200 Subject: [PATCH 1/3] Improve hash map (#12678) * Mutable _Item * document falsy item * resize_down: expected test result * resize_down: actual result This is a problem since it causes rapid cycling * improve comment about falsy item Co-authored-by: Andrey * fix long line * Update hash_map.py * Update hash_map.py --------- Co-authored-by: Andrey Co-authored-by: Maxim Smolskiy --- data_structures/hashing/hash_map.py | 30 +++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 9213d6930f67..8c56c327a492 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -16,7 +16,7 @@ VAL = TypeVar("VAL") -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class _Item(Generic[KEY, VAL]): key: KEY val: VAL @@ -72,16 +72,17 @@ def _try_set(self, ind: int, key: KEY, val: VAL) -> bool: If bucket is empty or key is the same, does insert and return True. - If bucket has another key or deleted placeholder, - that means that we need to check next bucket. + If bucket has another key that means that we need to check next bucket. """ stored = self._buckets[ind] if not stored: + # A falsy item means that bucket was never used (None) + # or was deleted (_deleted). self._buckets[ind] = _Item(key, val) self._len += 1 return True elif stored.key == key: - self._buckets[ind] = _Item(key, val) + stored.val = val return True else: return False @@ -228,6 +229,27 @@ def __delitem__(self, key: KEY) -> None: Traceback (most recent call last): ... KeyError: 4 + + # Test resize down when sparse + ## Setup: resize up + >>> hm = HashMap(initial_block_size=100, capacity_factor=0.75) + >>> len(hm._buckets) + 100 + >>> for i in range(75): + ... hm[i] = i + >>> len(hm._buckets) + 100 + >>> hm[75] = 75 + >>> len(hm._buckets) + 200 + + ## Resize down + >>> del hm[75] + >>> len(hm._buckets) + 200 + >>> del hm[74] + >>> len(hm._buckets) + 100 """ for ind in self._iterate_buckets(key): item = self._buckets[ind] From e1115b5f15afa44deb4752483b9b456457f7e683 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Fri, 23 May 2025 00:07:43 +0300 Subject: [PATCH 2/3] Add tests and cleanup sum_of_subsets algorithm (#12746) * Add tests and cleanup sum_of_subsets algorithm. * Update sum_of_subsets.py * Update sum_of_subsets.py * Update sum_of_subsets.py * Update sum_of_subsets.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Maxim Smolskiy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- backtracking/sum_of_subsets.py | 45 ++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index f34d3ca34339..f26f179f8725 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -1,5 +1,5 @@ """ -The sum-of-subsetsproblem states that a set of non-negative integers, and a +The sum-of-subsets problem states that a set of non-negative integers, and a value M, determine all possible subsets of the given set whose summation sum equal to given M. @@ -7,10 +7,20 @@ can be used only once. """ -from __future__ import annotations +def generate_sum_of_subsets_solutions(nums: list[int], max_sum: int) -> list[list[int]]: + """ + The main function. For list of numbers 'nums' find the subsets with sum + equal to 'max_sum' + + >>> generate_sum_of_subsets_solutions(nums=[3, 34, 4, 12, 5, 2], max_sum=9) + [[3, 4, 2], [4, 5]] + >>> generate_sum_of_subsets_solutions(nums=[3, 34, 4, 12, 5, 2], max_sum=3) + [[3]] + >>> generate_sum_of_subsets_solutions(nums=[3, 34, 4, 12, 5, 2], max_sum=1) + [] + """ -def generate_sum_of_subsets_soln(nums: list[int], max_sum: int) -> list[list[int]]: result: list[list[int]] = [] path: list[int] = [] num_index = 0 @@ -34,7 +44,21 @@ def create_state_space_tree( This algorithm follows depth-fist-search and backtracks when the node is not branchable. + >>> path = [] + >>> result = [] + >>> create_state_space_tree( + ... nums=[1], + ... max_sum=1, + ... num_index=0, + ... path=path, + ... result=result, + ... remaining_nums_sum=1) + >>> path + [] + >>> result + [[1]] """ + if sum(path) > max_sum or (remaining_nums_sum + sum(path)) < max_sum: return if sum(path) == max_sum: @@ -51,16 +75,7 @@ def create_state_space_tree( ) -""" -remove the comment to take an input from the user - -print("Enter the elements") -nums = list(map(int, input().split())) -print("Enter max_sum sum") -max_sum = int(input()) +if __name__ == "__main__": + import doctest -""" -nums = [3, 34, 4, 12, 5, 2] -max_sum = 9 -result = generate_sum_of_subsets_soln(nums, max_sum) -print(*result) + doctest.testmod() From a8ad2db2b966fcbd2e6a82b8f1544116838c02b0 Mon Sep 17 00:00:00 2001 From: Mindaugas <76015221+mindaugl@users.noreply.github.com> Date: Fri, 23 May 2025 00:17:48 +0300 Subject: [PATCH 3/3] Update matrix_chain_order calculation with more details and test. (#12759) --- dynamic_programming/matrix_chain_order.py | 33 ++++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/dynamic_programming/matrix_chain_order.py b/dynamic_programming/matrix_chain_order.py index d612aea7b99d..6df43e84be28 100644 --- a/dynamic_programming/matrix_chain_order.py +++ b/dynamic_programming/matrix_chain_order.py @@ -5,13 +5,19 @@ Implementation of Matrix Chain Multiplication Time Complexity: O(n^3) Space Complexity: O(n^2) + +Reference: https://en.wikipedia.org/wiki/Matrix_chain_multiplication """ -def matrix_chain_order(array): +def matrix_chain_order(array: list[int]) -> tuple[list[list[int]], list[list[int]]]: + """ + >>> matrix_chain_order([10, 30, 5]) + ([[0, 0, 0], [0, 0, 1500], [0, 0, 0]], [[0, 0, 0], [0, 0, 1], [0, 0, 0]]) + """ n = len(array) - matrix = [[0 for x in range(n)] for x in range(n)] - sol = [[0 for x in range(n)] for x in range(n)] + matrix = [[0 for _ in range(n)] for _ in range(n)] + sol = [[0 for _ in range(n)] for _ in range(n)] for chain_length in range(2, n): for a in range(1, n - chain_length + 1): @@ -28,26 +34,33 @@ def matrix_chain_order(array): return matrix, sol -# Print order of matrix with Ai as Matrix -def print_optiomal_solution(optimal_solution, i, j): +def print_optimal_solution(optimal_solution: list[list[int]], i: int, j: int): + """ + Print order of matrix with Ai as Matrix. + """ + if i == j: print("A" + str(i), end=" ") else: print("(", end=" ") - print_optiomal_solution(optimal_solution, i, optimal_solution[i][j]) - print_optiomal_solution(optimal_solution, optimal_solution[i][j] + 1, j) + print_optimal_solution(optimal_solution, i, optimal_solution[i][j]) + print_optimal_solution(optimal_solution, optimal_solution[i][j] + 1, j) print(")", end=" ") def main(): + """ + Size of matrix created from array [30, 35, 15, 5, 10, 20, 25] will be: + 30*35 35*15 15*5 5*10 10*20 20*25 + """ + array = [30, 35, 15, 5, 10, 20, 25] n = len(array) - # Size of matrix created from above array will be - # 30*35 35*15 15*5 5*10 10*20 20*25 + matrix, optimal_solution = matrix_chain_order(array) print("No. of Operation required: " + str(matrix[1][n - 1])) - print_optiomal_solution(optimal_solution, 1, n - 1) + print_optimal_solution(optimal_solution, 1, n - 1) if __name__ == "__main__":