From 29eb341e85fcfa42351ce066b1abaf9975645eb4 Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 7 Jun 2023 22:43:42 +0100 Subject: [PATCH 1/9] feat: Create number container system algorithm --- other/number_container_system.py | 128 +++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 other/number_container_system.py diff --git a/other/number_container_system.py b/other/number_container_system.py new file mode 100644 index 000000000000..ef3c81f3dc9c --- /dev/null +++ b/other/number_container_system.py @@ -0,0 +1,128 @@ +""" +A number container system that uses binary search to +delete and insert values into arrays with O(n logn) +write times and O(1) read times. + +This container system holds integers at indexes. + +Further explained in this leetcode problem +> https://leetcode.com/problems/minimum-cost-tree-from-leaf-values +""" + + +class NumberContainer: + def __init__(self): + # Holds number as the key and returns list of indexes where the number is + # The list of indexes is a sorted array in ascending order + self.numbermap: dict[int, list[int]] = {} + # Simply holds each index and it's number + self.indexmap: dict[int, int] = {} + + def binary_search_delete(self, array: list[int], item: int) -> list[int]: + """ + Removes the item from the array and returns + the new array. + + >>> NumberContainer().binary_search_delete([1,2,3], 2) + [1, 3] + >>> NumberContainer().binary_search_delete([], 0) + Traceback (most recent call last): + ... + ValueError: The item is not in the array, and therefore cannot be deleted + """ + low = 0 + high = len(array) - 1 + + while low <= high: + mid = (low + high) // 2 + if array[mid] == item: + array.pop(mid) + return array + elif array[mid] < item: + low = mid + 1 + else: + high = mid - 1 + raise ValueError("The item is not in the array, and therefore cannot be deleted") + + def binary_search_insert(self, array: list[int], index: int) -> list[int]: + """ + Inserts the index into the sorted array + at the correct position + + >>> NumberContainer().binary_search_insert([1,2,3], 2) + [1, 2, 2, 3] + >>> NumberContainer().binary_search_insert([0,1,3], 2) + [0, 1, 2, 3] + """ + low = 0 + high = len(array) - 1 + + while low <= high: + mid = (low + high) // 2 + if array[mid] == index: + # If the item already exists in the array, insert it after the existing item + array.insert(mid + 1, index) + return array + elif array[mid] < index: + low = mid + 1 + else: + high = mid - 1 + + # If the item doesn't exist in the array, insert it at the appropriate position + array.insert(low, index) + return array + + def change(self, index: int, number: int) -> None: + """ + Changes (sets) the index as number + + >>> cont = NumberContainer() + >>> cont.change(0, 10) + >>> cont.change(0, 20) + >>> cont.change(-1, 20) + """ + # Remove previous index + if index in self.indexmap: + n = self.indexmap[index] + if len(self.numbermap[n]) == 1: + del self.numbermap[n] + else: + self.numbermap[n] = self.binary_search_delete(self.numbermap[n], index) + + # Set new index + self.indexmap[index] = number + + # Number not seen before or empty so insert number value + if not number in self.numbermap: + self.numbermap[number] = [index] + + # Here we need to perform a binary search insertion in order to insert + # The item in the correct place + else: + self.numbermap[number] = self.binary_search_insert( + self.numbermap[number], index + ) + + def find(self, number: int) -> int: + """ + Returns the smallest index where the number is. + + >>> cont = NumberContainer() + >>> cont.find(10) + -1 + >>> cont.change(0, 10) + >>> cont.find(10) + 0 + >>> cont.change(0, 20) + >>> cont.find(10) + -1 + >>> cont.find(20) + 0 + """ + # Simply return the 0th index (smallest) of the indexes found (or -1) + return self.numbermap.get(number, [-1])[0] + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1eb503983720fb701ca1908d4cc8077d10ae8efd Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:44:19 +0000 Subject: [PATCH 2/9] updating DIRECTORY.md --- DIRECTORY.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 231b0e2f1d2f..6dac4a9a5783 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -419,8 +419,9 @@ * [Frequent Pattern Graph Miner](graphs/frequent_pattern_graph_miner.py) * [G Topological Sort](graphs/g_topological_sort.py) * [Gale Shapley Bigraph](graphs/gale_shapley_bigraph.py) + * [Graph Adjacency List](graphs/graph_adjacency_list.py) + * [Graph Adjacency Matrix](graphs/graph_adjacency_matrix.py) * [Graph List](graphs/graph_list.py) - * [Graph Matrix](graphs/graph_matrix.py) * [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py) * [Greedy Best First](graphs/greedy_best_first.py) * [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py) @@ -479,6 +480,7 @@ * [Lib](linear_algebra/src/lib.py) * [Polynom For Points](linear_algebra/src/polynom_for_points.py) * [Power Iteration](linear_algebra/src/power_iteration.py) + * [Rank Of Matrix](linear_algebra/src/rank_of_matrix.py) * [Rayleigh Quotient](linear_algebra/src/rayleigh_quotient.py) * [Schur Complement](linear_algebra/src/schur_complement.py) * [Test Linear Algebra](linear_algebra/src/test_linear_algebra.py) @@ -651,6 +653,7 @@ * [Sigmoid Linear Unit](maths/sigmoid_linear_unit.py) * [Signum](maths/signum.py) * [Simpson Rule](maths/simpson_rule.py) + * [Simultaneous Linear Equation Solver](maths/simultaneous_linear_equation_solver.py) * [Sin](maths/sin.py) * [Sock Merchant](maths/sock_merchant.py) * [Softmax](maths/softmax.py) @@ -726,6 +729,7 @@ * [Maximum Subarray](other/maximum_subarray.py) * [Maximum Subsequence](other/maximum_subsequence.py) * [Nested Brackets](other/nested_brackets.py) + * [Number Container System](other/number_container_system.py) * [Password](other/password.py) * [Quine](other/quine.py) * [Scoring Algorithm](other/scoring_algorithm.py) From 4743acadb1cd443afd8a5ad667d09c87b7ac9eaa Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 7 Jun 2023 22:45:40 +0100 Subject: [PATCH 3/9] chore: Fix failing tests --- other/number_container_system.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index ef3c81f3dc9c..805896c5d8c8 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -1,6 +1,6 @@ """ -A number container system that uses binary search to -delete and insert values into arrays with O(n logn) +A number container system that uses binary search to +delete and insert values into arrays with O(n logn) write times and O(1) read times. This container system holds integers at indexes. @@ -11,7 +11,7 @@ class NumberContainer: - def __init__(self): + def __init__(self) -> None: # Holds number as the key and returns list of indexes where the number is # The list of indexes is a sorted array in ascending order self.numbermap: dict[int, list[int]] = {} @@ -42,13 +42,15 @@ def binary_search_delete(self, array: list[int], item: int) -> list[int]: low = mid + 1 else: high = mid - 1 - raise ValueError("The item is not in the array, and therefore cannot be deleted") + raise ValueError( + "The item is not in the array, and therefore cannot be deleted" + ) def binary_search_insert(self, array: list[int], index: int) -> list[int]: """ Inserts the index into the sorted array at the correct position - + >>> NumberContainer().binary_search_insert([1,2,3], 2) [1, 2, 2, 3] >>> NumberContainer().binary_search_insert([0,1,3], 2) @@ -60,7 +62,8 @@ def binary_search_insert(self, array: list[int], index: int) -> list[int]: while low <= high: mid = (low + high) // 2 if array[mid] == index: - # If the item already exists in the array, insert it after the existing item + # If the item already exists in the array, + # insert it after the existing item array.insert(mid + 1, index) return array elif array[mid] < index: @@ -93,7 +96,7 @@ def change(self, index: int, number: int) -> None: self.indexmap[index] = number # Number not seen before or empty so insert number value - if not number in self.numbermap: + if number not in self.numbermap: self.numbermap[number] = [index] # Here we need to perform a binary search insertion in order to insert @@ -121,7 +124,8 @@ def find(self, number: int) -> int: """ # Simply return the 0th index (smallest) of the indexes found (or -1) return self.numbermap.get(number, [-1])[0] - + + if __name__ == "__main__": import doctest From 82e1a0bbba3b3b48f3674f05c0a43a7ef65450d9 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 8 Jun 2023 09:48:50 +0100 Subject: [PATCH 4/9] Update other/number_container_system.py Co-authored-by: Christian Clauss --- other/number_container_system.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index 805896c5d8c8..31c56164e185 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -1,7 +1,6 @@ """ -A number container system that uses binary search to -delete and insert values into arrays with O(n logn) -write times and O(1) read times. +A number container system that uses binary search to delete and insert values into +arrays with O(n logn) write times and O(1) read times. This container system holds integers at indexes. From c95797b4aaac72cd9e51dd403527f1c69e237dcc Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 8 Jun 2023 09:49:04 +0100 Subject: [PATCH 5/9] Update other/number_container_system.py Co-authored-by: Christian Clauss --- other/number_container_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index 31c56164e185..3c9c6184afa9 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -11,8 +11,8 @@ class NumberContainer: def __init__(self) -> None: - # Holds number as the key and returns list of indexes where the number is - # The list of indexes is a sorted array in ascending order + # numbermap keys are the number and its values are lists of indexes sorted + # in ascending order self.numbermap: dict[int, list[int]] = {} # Simply holds each index and it's number self.indexmap: dict[int, int] = {} From 9bf70fec275dd229309839ae6c600daf1aaaaa01 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 8 Jun 2023 09:49:10 +0100 Subject: [PATCH 6/9] Update other/number_container_system.py Co-authored-by: Christian Clauss --- other/number_container_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index 3c9c6184afa9..b3ae13e0d8b7 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -14,7 +14,7 @@ def __init__(self) -> None: # numbermap keys are the number and its values are lists of indexes sorted # in ascending order self.numbermap: dict[int, list[int]] = {} - # Simply holds each index and it's number + # indexmap keys are an index and it's values are the number at that index self.indexmap: dict[int, int] = {} def binary_search_delete(self, array: list[int], item: int) -> list[int]: From 232215349108eb2038db8632010f028bcd4e0c46 Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Thu, 8 Jun 2023 11:13:46 +0100 Subject: [PATCH 7/9] chore: Add more tests --- other/number_container_system.py | 57 ++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index b3ae13e0d8b7..b9db6ffb4e45 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -19,16 +19,45 @@ def __init__(self) -> None: def binary_search_delete(self, array: list[int], item: int) -> list[int]: """ - Removes the item from the array and returns + Removes the item from the sorted array and returns the new array. >>> NumberContainer().binary_search_delete([1,2,3], 2) [1, 3] - >>> NumberContainer().binary_search_delete([], 0) + >>> NumberContainer().binary_search_delete([0, 0, 0], 0) + [0, 0] + >>> NumberContainer().binary_search_delete([-1, -1, -1], -1) + [-1, -1] + >>> NumberContainer().binary_search_delete([-1, 0], 0) + [-1] + >>> NumberContainer().binary_search_delete([-1, 0], -1) + [0] + >>> NumberContainer().binary_search_delete(range(7), 3) + [0, 1, 2, 4, 5, 6] + >>> NumberContainer().binary_search_delete([1.1, 2.2, 3.3], 2.2) + [1.1, 3.3] + >>> NumberContainer().binary_search_delete("abcde", "c") + ['a', 'b', 'd', 'e'] + >>> NumberContainer().binary_search_delete([0, -1, 2, 4], 0) Traceback (most recent call last): ... - ValueError: The item is not in the array, and therefore cannot be deleted + ValueError: Either the item is not in the array or the array was unsorted + >>> NumberContainer().binary_search_delete([2, 0, 4, -1, 11], -1) + Traceback (most recent call last): + ... + ValueError: Either the item is not in the array or the array was unsorted + >>> NumberContainer().binary_search_delete(125, 1) + Traceback (most recent call last): + ... + TypeError: binary_search_delete() only accepts either a list, range or str """ + if isinstance(array, (range, str)): + array = list(array) + elif not isinstance(array, list): + raise TypeError( + "binary_search_delete() only accepts either a list, range or str" + ) + low = 0 high = len(array) - 1 @@ -42,19 +71,34 @@ def binary_search_delete(self, array: list[int], item: int) -> list[int]: else: high = mid - 1 raise ValueError( - "The item is not in the array, and therefore cannot be deleted" + "Either the item is not in the array or the array was unsorted" ) def binary_search_insert(self, array: list[int], index: int) -> list[int]: """ Inserts the index into the sorted array - at the correct position + at the correct position. >>> NumberContainer().binary_search_insert([1,2,3], 2) [1, 2, 2, 3] >>> NumberContainer().binary_search_insert([0,1,3], 2) [0, 1, 2, 3] + >>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 103], 51) + [-5, -3, 0, 0, 11, 51, 103] + >>> NumberContainer().binary_search_insert([-5, -3, 0, 0, 11, 100, 103], 101) + [-5, -3, 0, 0, 11, 100, 101, 103] + >>> NumberContainer().binary_search_insert(range(10), 4) + [0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9] + >>> NumberContainer().binary_search_insert("abd", "c") + ['a', 'b', 'c', 'd'] """ + if isinstance(array, (range, str)): + array = list(array) + elif not isinstance(array, list): + raise TypeError( + "binary_search_insert() only accepts either a list, range or str" + ) + low = 0 high = len(array) - 1 @@ -81,7 +125,8 @@ def change(self, index: int, number: int) -> None: >>> cont = NumberContainer() >>> cont.change(0, 10) >>> cont.change(0, 20) - >>> cont.change(-1, 20) + >>> cont.change(-13, 20) + >>> cont.change(-100030, 20032903290) """ # Remove previous index if index in self.indexmap: From c6c021aeff4f397c765873e3e7fa26033c67a45e Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Thu, 8 Jun 2023 11:16:30 +0100 Subject: [PATCH 8/9] chore: Create binary_search_insert failing test --- other/number_container_system.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/other/number_container_system.py b/other/number_container_system.py index b9db6ffb4e45..39e5d2b81224 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -91,6 +91,10 @@ def binary_search_insert(self, array: list[int], index: int) -> list[int]: [0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9] >>> NumberContainer().binary_search_insert("abd", "c") ['a', 'b', 'c', 'd'] + >>> NumberContainer().binary_search_insert(131, 23) + Traceback (most recent call last): + ... + TypeError: binary_search_insert() only accepts either a list, range or str """ if isinstance(array, (range, str)): array = list(array) From f9801ac4528b6609dcd41eecd23497e9a2c8ea9f Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Thu, 8 Jun 2023 11:20:18 +0100 Subject: [PATCH 9/9] type: Update typehints to accept str, list and range --- other/number_container_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/other/number_container_system.py b/other/number_container_system.py index 39e5d2b81224..f547bc8a229e 100644 --- a/other/number_container_system.py +++ b/other/number_container_system.py @@ -17,7 +17,7 @@ def __init__(self) -> None: # indexmap keys are an index and it's values are the number at that index self.indexmap: dict[int, int] = {} - def binary_search_delete(self, array: list[int], item: int) -> list[int]: + def binary_search_delete(self, array: list | str | range, item: int) -> list[int]: """ Removes the item from the sorted array and returns the new array. @@ -74,7 +74,7 @@ def binary_search_delete(self, array: list[int], item: int) -> list[int]: "Either the item is not in the array or the array was unsorted" ) - def binary_search_insert(self, array: list[int], index: int) -> list[int]: + def binary_search_insert(self, array: list | str | range, index: int) -> list[int]: """ Inserts the index into the sorted array at the correct position.