From ef61a3fa76960482898a74c30be4ab99583d7e4d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Sat, 5 Mar 2022 01:42:31 +0200 Subject: [PATCH 1/8] Add BIT2D --- pydatastructs/trees/binary_trees.py | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 34b265144..c099b0a36 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -3,6 +3,7 @@ from pydatastructs.utils import TreeNode, CartesianTreeNode, RedBlackTreeNode from pydatastructs.miscellaneous_data_structures import Stack from pydatastructs.linear_data_structures import OneDimensionalArray +from pydatastructs.linear_data_structures import MultiDimensionalArray from pydatastructs.linear_data_structures.arrays import ArrayForTrees from pydatastructs.utils.misc_util import ( Backend, raise_if_backend_is_not_python) @@ -1728,3 +1729,46 @@ def get_sum(self, left_index, right_index): self.get_prefix_sum(left_index - 1) else: return self.get_prefix_sum(right_index) + + class BinaryIndexedTree2D(object): + __slots__ = ['tree', 'array', ] + + def __new__(cls, array, **kwargs): + raise_if_backend_is_not_python(cls, kwargs.get('backend', Backend.PYTHON)) + obj = object.__new__(cls) + obj.array = MultiDimensionalArray(type(array[0]), len(array) ,len(array[0])) + + obj.tree = [len(array[0]) *[0]] * len(array) + + for i in range(array): + for j in range(array[0]): + obj.update(i ,j , obj.array[i][j]) + + return obj + + @classmethod + def methods(cls): + return ['update', 'get_prefix_sum','get_rectangle'] + def update(self ,x , y , val ): + i = x+1 + while i<= len(self.tree): + j = y+1 + while j<= len(self.tree[0]): + self.tree[i-1][j-1] += val + j+= j & (-j) + i += i &(-i) + + def get_prefix_sum(self , x , y ): + res =0 + i = x + 1 + while i <= len(self.tree): + j = y + 1 + while j <= len(self.tree[0]): + res += self.tree[i - 1][j - 1] + j += j & (-j) + i += i & (-i) + return res + + def get_rectangle(self, start_x ,start_y ,end_x , end_y ): + return self.get_prefix_sum(end_x ,end_y)- self.get_prefix_sum(end_x , start_y-1 )\ + -self.get_prefix_sum(start_x -1 ,end_y) + self.get_prefix_sum(start_x-1 , start_y-1) From 2db84eba14ae0ce334f5b9d155736dc52712d820 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Sat, 5 Mar 2022 02:14:50 +0200 Subject: [PATCH 2/8] Fix typo in BIT2D --- pydatastructs/trees/binary_trees.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index c099b0a36..1c027b2ac 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1731,7 +1731,7 @@ def get_sum(self, left_index, right_index): return self.get_prefix_sum(right_index) class BinaryIndexedTree2D(object): - __slots__ = ['tree', 'array', ] + __slots__ = ['tree', 'array' ] def __new__(cls, array, **kwargs): raise_if_backend_is_not_python(cls, kwargs.get('backend', Backend.PYTHON)) From ecca0c1aebd955888699db1189cfdc1f09bc17bf Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Sat, 5 Mar 2022 23:30:57 +0200 Subject: [PATCH 3/8] Fix arrays , Add successful test --- pydatastructs/trees/__init__.py | 3 +- pydatastructs/trees/binary_trees.py | 103 ++++++++++-------- .../trees/tests/test_binary_trees.py | 27 ++++- 3 files changed, 88 insertions(+), 45 deletions(-) diff --git a/pydatastructs/trees/__init__.py b/pydatastructs/trees/__init__.py index 6b9df8a22..a1f9496b6 100644 --- a/pydatastructs/trees/__init__.py +++ b/pydatastructs/trees/__init__.py @@ -16,7 +16,8 @@ CartesianTree, Treap, SplayTree, - RedBlackTree + RedBlackTree, + BinaryIndexedTree2D ) __all__.extend(binary_trees.__all__) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 1c027b2ac..93d53ce62 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -17,7 +17,8 @@ 'CartesianTree', 'Treap', 'SplayTree', - 'RedBlackTree' + 'RedBlackTree', + 'BinaryIndexedTree2D' ] class BinaryTree(object): @@ -1730,45 +1731,61 @@ def get_sum(self, left_index, right_index): else: return self.get_prefix_sum(right_index) - class BinaryIndexedTree2D(object): - __slots__ = ['tree', 'array' ] - - def __new__(cls, array, **kwargs): - raise_if_backend_is_not_python(cls, kwargs.get('backend', Backend.PYTHON)) - obj = object.__new__(cls) - obj.array = MultiDimensionalArray(type(array[0]), len(array) ,len(array[0])) - - obj.tree = [len(array[0]) *[0]] * len(array) - - for i in range(array): - for j in range(array[0]): - obj.update(i ,j , obj.array[i][j]) - - return obj - - @classmethod - def methods(cls): - return ['update', 'get_prefix_sum','get_rectangle'] - def update(self ,x , y , val ): - i = x+1 - while i<= len(self.tree): - j = y+1 - while j<= len(self.tree[0]): - self.tree[i-1][j-1] += val - j+= j & (-j) - i += i &(-i) - - def get_prefix_sum(self , x , y ): - res =0 - i = x + 1 - while i <= len(self.tree): - j = y + 1 - while j <= len(self.tree[0]): - res += self.tree[i - 1][j - 1] - j += j & (-j) - i += i & (-i) - return res - - def get_rectangle(self, start_x ,start_y ,end_x , end_y ): - return self.get_prefix_sum(end_x ,end_y)- self.get_prefix_sum(end_x , start_y-1 )\ - -self.get_prefix_sum(start_x -1 ,end_y) + self.get_prefix_sum(start_x-1 , start_y-1) +class BinaryIndexedTree2D(object): + """ + 2D Fenwick tree aka Binary indexed tree(BIT) + + """ + __slots__ = ['tree', 'array' , 'n' ,'m'] + + def __new__(cls, array, **kwargs): + raise_if_backend_is_not_python(cls, kwargs.get('backend', Backend.PYTHON)) + obj = object.__new__(cls) + obj.n = len(array) + obj.m = len(array[0]) + obj.array = [[0 for i in range(obj.m+1)] for j in range(obj.n+1)] + obj.tree = [[0 for i in range(obj.m+1 )] for j in range(obj.n+1 )] + + + for i in obj.array: + for j in i : + j = 0 + for i in obj.tree: + for j in i: + j = 0 + + for i in range(obj.n): + for j in range(obj.m): + obj.array[i][j] = array[i][j] + + for i in range(obj.n): + for j in range(obj.m): + obj.update(i ,j , obj.array[i][j]) + return obj + + @classmethod + def methods(cls): + return ['update', 'get_prefix_sum','get_rectangle'] + def update(self ,x , y , val ): + i = x +1 + while i<= self.n: + j = y +1 + while j<= self.m: + self.tree[i-1][j-1] += val + j+= j & (-j) + i += i &(-i) + + def get_prefix_sum(self , x , y ): + res =0 + i = x +1 + while i >0: + j = y +1 + while j >0 : + res += self.tree[i - 1][j - 1] + j -= j & (-j) + i -= i & (-i) + return res + + def get_rectangle(self, start_x ,start_y ,end_x , end_y ): + return self.get_prefix_sum(end_x ,end_y)- self.get_prefix_sum(end_x , start_y-1 )\ + -self.get_prefix_sum(start_x -1 ,end_y) + self.get_prefix_sum(start_x-1 , start_y-1) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index ea9ea5bf3..7bfaf025f 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -1,8 +1,9 @@ from pydatastructs.trees.binary_trees import ( BinarySearchTree, BinaryTreeTraversal, AVLTree, - ArrayForTrees, BinaryIndexedTree, SelfBalancingBinaryTree, SplayTree, CartesianTree, Treap, RedBlackTree) + ArrayForTrees, BinaryIndexedTree, SelfBalancingBinaryTree, SplayTree, CartesianTree, Treap, RedBlackTree , ) from pydatastructs.utils.raises_util import raises from pydatastructs.utils.misc_util import TreeNode +from pydatastructs.trees.binary_trees import BinaryIndexedTree2D from copy import deepcopy import random @@ -684,3 +685,27 @@ def test_RedBlackTree(): pre_order = trav.depth_first_search(order='pre_order') assert [node.key for node in in_order] == [2, 5, 6, 15, 20] assert [node.key for node in pre_order] == [6, 5, 2, 20, 15] + + +def test_BinaryIndexedTree2D(): + sample = [ + [1 ,2 ,3 ,4 ], + [5 , 6,7 ,8 ], + [9 ,10 ,11 ,12], + [13 ,14 ,15 ,16] + ] + BIT = BinaryIndexedTree2D + bit2d = BIT(sample) + def sumRect(i , j ): + sum =0 + for x in range (0, i +1 ): + for y in range (0, j+1 ): + sum += sample[x][y] + return sum + + for i in range(0 , len(sample)) : + for j in range(0 , len(sample[0]) ) : + assert sumRect(i ,j ) == bit2d.get_prefix_sum(i , j ) + + + From 486b2016662f1f46714afc83ff93e48fcb51dd1c Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Sun, 6 Mar 2022 02:04:52 +0200 Subject: [PATCH 4/8] Add another random tests and add docstring --- docs/source/pydatastructs/pydatastructs.rst | 2 +- .../pydatastructs/trees/binary_trees.rst | 3 + pydatastructs/trees/binary_trees.py | 82 +++++++++++++++++-- .../trees/tests/test_binary_trees.py | 41 +++++++--- 4 files changed, 108 insertions(+), 20 deletions(-) diff --git a/docs/source/pydatastructs/pydatastructs.rst b/docs/source/pydatastructs/pydatastructs.rst index cb33622c7..941f214be 100644 --- a/docs/source/pydatastructs/pydatastructs.rst +++ b/docs/source/pydatastructs/pydatastructs.rst @@ -10,4 +10,4 @@ Modules graphs/graphs.rst strings/strings.rst trees/trees.rst - miscellaneous_data_structures/miscellaneous_data_structures.rst \ No newline at end of file + miscellaneous_data_structures/miscellaneous_data_structures.rst diff --git a/docs/source/pydatastructs/trees/binary_trees.rst b/docs/source/pydatastructs/trees/binary_trees.rst index f24e21621..a5d63b468 100644 --- a/docs/source/pydatastructs/trees/binary_trees.rst +++ b/docs/source/pydatastructs/trees/binary_trees.rst @@ -9,6 +9,8 @@ Binary Trees .. autoclass:: pydatastructs.BinaryIndexedTree +.. autoclass:: pydatastructs.BinaryIndexedTree2D + .. autoclass:: pydatastructs.CartesianTree .. autoclass:: pydatastructs.Treap @@ -18,3 +20,4 @@ Binary Trees .. autoclass:: pydatastructs.RedBlackTree .. autoclass:: pydatastructs.BinaryTreeTraversal + diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 93d53ce62..0b2b037f3 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1735,6 +1735,29 @@ class BinaryIndexedTree2D(object): """ 2D Fenwick tree aka Binary indexed tree(BIT) + Parameters + ======== + + array: 2d list/tuple + The array whose elements are to be + considered for the queries. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples + ======== + + >>> from pydatastructs import BinaryIndexedTree2D + >>> bit = BinaryIndexedTree([[1, 2, 3] ,\ + [2 , 3 ,4 ] ,\ + [8 ,6 ,7 ]]) + >>> bit.get_area(0, 0 , 1 ,1 ) + 8 + >>> bit.add(1 ,1 , 3) + >>> bit.get_sum(0, 0 , 1 , 1 ) + 11 + """ __slots__ = ['tree', 'array' , 'n' ,'m'] @@ -1760,13 +1783,32 @@ def __new__(cls, array, **kwargs): for i in range(obj.n): for j in range(obj.m): - obj.update(i ,j , obj.array[i][j]) + obj.add(i ,j , obj.array[i][j]) return obj @classmethod def methods(cls): - return ['update', 'get_prefix_sum','get_rectangle'] - def update(self ,x , y , val ): + return ['add','get_area'] + def add(self ,x , y , val ): + """ + Add `val` to your value in position [x][y] aka yorArr[x][y]+=val + + Parameters + ======== + + x :int + x of of point + y :int + of point + val :int + value to add + + Returns + ========= + + None + + """ i = x +1 while i<= self.n: j = y +1 @@ -1775,7 +1817,7 @@ def update(self ,x , y , val ): j+= j & (-j) i += i &(-i) - def get_prefix_sum(self , x , y ): + def _get_sum_to_origin(self , x , y ): res =0 i = x +1 while i >0: @@ -1786,6 +1828,32 @@ def get_prefix_sum(self , x , y ): i -= i & (-i) return res - def get_rectangle(self, start_x ,start_y ,end_x , end_y ): - return self.get_prefix_sum(end_x ,end_y)- self.get_prefix_sum(end_x , start_y-1 )\ - -self.get_prefix_sum(start_x -1 ,end_y) + self.get_prefix_sum(start_x-1 , start_y-1) + def get_area(self, start_x ,start_y ,end_x , end_y ): + """ + Get area of rectangle that has up left corner of point(start_x , start_y ) and down right coener of (end_x , end_y).\n + let S = start point and E = end point + Saaaaa\n + aaaaaa\n + aaaaaa\n + aaaaaE\n + + Parameters + ========== + + start_x :int + x of start point + start_y :int + y of start point + end_x :int + x of end point + end_y :int + y of end point + + Returns + ======== + sum:int + sum of elements of rectangle that has up left corner of point(start_x , start_y ) and down right coener of (end_x , end_y) + + """ + return self._get_sum_to_origin(end_x ,end_y)- self._get_sum_to_origin(end_x , start_y-1 )\ + -self._get_sum_to_origin(start_x -1 ,end_y) + self._get_sum_to_origin(start_x-1 , start_y-1) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 7bfaf025f..cddeda8bb 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -688,24 +688,41 @@ def test_RedBlackTree(): def test_BinaryIndexedTree2D(): - sample = [ - [1 ,2 ,3 ,4 ], - [5 , 6,7 ,8 ], - [9 ,10 ,11 ,12], - [13 ,14 ,15 ,16] - ] + + n = random.randint(10, 40) + m = random.randint(10 ,40) + sample = [[random.randint(0, 10000) for i in range(m)] for j in range(n)] + BIT = BinaryIndexedTree2D bit2d = BIT(sample) - def sumRect(i , j ): + def sumRect(sample ,i , j , l ,k ): sum =0 - for x in range (0, i +1 ): - for y in range (0, j+1 ): + for x in range (l, i +1 ): + for y in range (k, j+1 ): sum += sample[x][y] return sum - for i in range(0 , len(sample)) : - for j in range(0 , len(sample[0]) ) : - assert sumRect(i ,j ) == bit2d.get_prefix_sum(i , j ) + for i in range(0 , len(sample)): + for j in range(0 , len(sample[0]) ): + for l in range (i , len(sample) ): + for k in range (j , len(sample[0])): + message = f"for (i , j ) = ({i} ,{j}) ,(l , k ) = ({l} ,{k})" + assert sumRect(sample , l , k , i , j ) == bit2d.get_area(i, j , l ,k ) , message + + for i in range(20) : + x = random.randint(0 , n-1) + y = random.randint(0 , m-1) + val = random.randint(0 , 100) + bit2d.add(x , y , val ) + sample[x][y] += val + + for i in range(0 , len(sample)): + for j in range(0 , len(sample[0]) ): + for l in range (i , len(sample) ): + for k in range (j , len(sample[0])): + message = f"for (i , j ) = ({i} ,{j}) ,(l , k ) = ({l} ,{k}) , After randoms updates" + assert sumRect(sample , l , k , i , j ) == bit2d.get_area(i, j , l ,k ) , message + From e97a915f4fde5e9c1eefdaafd6a5e13599575f78 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Tue, 8 Mar 2022 23:37:40 +0200 Subject: [PATCH 5/8] Add initial Nd BIT --- pydatastructs/trees/__init__.py | 3 +- pydatastructs/trees/binary_trees.py | 74 ++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/pydatastructs/trees/__init__.py b/pydatastructs/trees/__init__.py index a1f9496b6..c8715c1c7 100644 --- a/pydatastructs/trees/__init__.py +++ b/pydatastructs/trees/__init__.py @@ -17,7 +17,8 @@ Treap, SplayTree, RedBlackTree, - BinaryIndexedTree2D + BinaryIndexedTree2D, + BinaryIndexedTreeNd ) __all__.extend(binary_trees.__all__) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 0b2b037f3..cf3345296 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -18,7 +18,9 @@ 'Treap', 'SplayTree', 'RedBlackTree', - 'BinaryIndexedTree2D' + 'BinaryIndexedTree2D', + 'BinaryIndexedTreeNd' + ] class BinaryTree(object): @@ -1857,3 +1859,73 @@ def get_area(self, start_x ,start_y ,end_x , end_y ): """ return self._get_sum_to_origin(end_x ,end_y)- self._get_sum_to_origin(end_x , start_y-1 )\ -self._get_sum_to_origin(start_x -1 ,end_y) + self._get_sum_to_origin(start_x-1 , start_y-1) + + +class BinaryIndexedTreeNd(object): + __slots__ = ['tree' , 'array' , 'limits' , 'backtrack_ind'] + + def __new__(cls, array, **kwargs): + raise_if_backend_is_not_python(cls , kwargs.get('backend' , Backend.PYTHON)) + obj = object.__new__(cls) + current_dimension = array + obj.limits =[] + while type(current_dimension)==list or type(current_dimension)==tuple : + obj.limits.append(len(current_dimension)) + current_dimension = current_dimension[0] + obj.array =[] + obj.array = obj.fillNdArray(0) + obj.tree = obj._fillNdArray(0) + obj.backtrack_ind =[] + @classmethod + def methods(cls): + return ['add','get_sum'] + + def _fillNdArray(self , number:int , ind:int =0 )->list: + level = [] + for i in range(self.limits[ind]): + if ind+1 < len(self.limits): + level.append(self.fillNdArray(number , ind+1)) + else : + level.append(number) + return level + def _get_element(self , position:tuple , arr:list) : + ind = 0 + cur = arr + while ind < len(self.limits): + cur = arr[position[ind]] + ind+=1 + return cur + def _add_to_element(self , val:int,position:tuple , arr:list): + ind =0 + cur = arr + while ind < len(self.limits): + cur = arr[position[ind]] + ind+=1 + cur += val + + def add(self , position:tuple , val:int , ind:int =0 ): + if ind == len(self.limits): + self._add_to_element(val, tuple(self.backtrack_ind), self.tree) + return + + i = position[ind] =1 + while i < self.limits[ind] : + + self.backtrack_ind.append(i) + self.add(position , val , ind+1 ) + self.backtrack_ind.pop() + + i -= i &(-i) + + def get_sum(self , position:tuple , ind :int ): + res =0 + if ind == len(self.limits): + res = self.get_sum(tuple(self.backtrack_ind)) + else : + i = position[ind]+1 + while i>0 : + self.backtrack_ind.append(i) + res += self.get_sum(position , ind+1 ) + self.backtrack_ind.pop() + i-= i& (-i) + return res From c7405505ab0b1758c1cd097fbb6af4f0c3984b67 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Wed, 9 Mar 2022 00:14:16 +0200 Subject: [PATCH 6/8] Add init sum and some testing --- pydatastructs/trees/binary_trees.py | 19 ++++++++++++++++++- .../trees/tests/test_binary_trees.py | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index cf3345296..c9f9292b6 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1876,10 +1876,22 @@ def __new__(cls, array, **kwargs): obj.array = obj.fillNdArray(0) obj.tree = obj._fillNdArray(0) obj.backtrack_ind =[] + obj.init_sum() + @classmethod def methods(cls): return ['add','get_sum'] + def init_sum(self , ind =0 ): + if ind == len(self.limits): + self.add(tuple(self.backtrack_ind) , self._get_element(tuple(list) ,self.array)) + else : + for i in range(len(self.limits[ind])): + self.backtrack_ind.append(i) + self.init_sum(ind+1 ) + self.backtrack_ind.pop() + + def _fillNdArray(self , number:int , ind:int =0 )->list: level = [] for i in range(self.limits[ind]): @@ -1888,6 +1900,7 @@ def _fillNdArray(self , number:int , ind:int =0 )->list: else : level.append(number) return level + def _get_element(self , position:tuple , arr:list) : ind = 0 cur = arr @@ -1895,6 +1908,7 @@ def _get_element(self , position:tuple , arr:list) : cur = arr[position[ind]] ind+=1 return cur + def _add_to_element(self , val:int,position:tuple , arr:list): ind =0 cur = arr @@ -1917,7 +1931,7 @@ def add(self , position:tuple , val:int , ind:int =0 ): i -= i &(-i) - def get_sum(self , position:tuple , ind :int ): + def _get_sum_to_origin(self , position:tuple , ind :int ): res =0 if ind == len(self.limits): res = self.get_sum(tuple(self.backtrack_ind)) @@ -1929,3 +1943,6 @@ def get_sum(self , position:tuple , ind :int ): self.backtrack_ind.pop() i-= i& (-i) return res + + def get_sum(self): + pass diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index cddeda8bb..0a0117841 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -4,6 +4,7 @@ from pydatastructs.utils.raises_util import raises from pydatastructs.utils.misc_util import TreeNode from pydatastructs.trees.binary_trees import BinaryIndexedTree2D +from pydatastructs.trees.binary_trees import BinaryIndexedTreeNd from copy import deepcopy import random @@ -726,3 +727,18 @@ def sumRect(sample ,i , j , l ,k ): +def test_BinaryIndexedTreeNd(): + n = random.randint(2, 3) + m = random.randint(2, 3) + k = random.randint(2, 3) + arr =[] + for i in range(n): + level1 = [] + for j in range(m): + level2 = [] + for l in range(k): + level2.append(random.randint(5 ,12)) + level1.append(level2) + arr.append(level1) + bits = BinaryIndexedTreeNd(arr) + From fae7f4fac14d75e62aad8d53b4ccdacf07fc29db Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Thu, 17 Mar 2022 18:34:55 +0200 Subject: [PATCH 7/8] Add `get_sum` and fixed some bugs --- pydatastructs/trees/binary_trees.py | 106 +++++++++++------- .../trees/tests/test_binary_trees.py | 11 +- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index c9f9292b6..56c532d64 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1862,6 +1862,9 @@ def get_area(self, start_x ,start_y ,end_x , end_y ): class BinaryIndexedTreeNd(object): + """ + testing + """ __slots__ = ['tree' , 'array' , 'limits' , 'backtrack_ind'] def __new__(cls, array, **kwargs): @@ -1872,77 +1875,102 @@ def __new__(cls, array, **kwargs): while type(current_dimension)==list or type(current_dimension)==tuple : obj.limits.append(len(current_dimension)) current_dimension = current_dimension[0] - obj.array =[] - obj.array = obj.fillNdArray(0) - obj.tree = obj._fillNdArray(0) obj.backtrack_ind =[] - obj.init_sum() + obj.array =[] + obj.array = obj._fillNdArray(0 , 0 , array) + obj.tree = obj._fillNdArray(0 , 0, None) + obj.init_sum() + return obj @classmethod def methods(cls): - return ['add','get_sum'] + return ['add','get_sum' ] - def init_sum(self , ind =0 ): + + + def init_sum(self , ind =0 , curPosition = []): if ind == len(self.limits): - self.add(tuple(self.backtrack_ind) , self._get_element(tuple(list) ,self.array)) + self.add(tuple(curPosition) , self._get_element(tuple(curPosition) ,self.array)) else : - for i in range(len(self.limits[ind])): - self.backtrack_ind.append(i) - self.init_sum(ind+1 ) - self.backtrack_ind.pop() + for i in range(self.limits[ind]): + self.init_sum(ind+1 , curPosition + [i]) - def _fillNdArray(self , number:int , ind:int =0 )->list: + + def _fillNdArray(self ,ind:int ,number:int , array:list , curPosition = [] )->list: level = [] - for i in range(self.limits[ind]): - if ind+1 < len(self.limits): - level.append(self.fillNdArray(number , ind+1)) - else : - level.append(number) + if ind == len(self.limits): + level = number + if array != None : + level = self._get_element(tuple(curPosition) , array) + else : + for i in range(self.limits[ind]): + level.append(self._fillNdArray(ind+1 , number , array , curPosition +[i])) + return level + + def _get_element(self , position:tuple , arr:list) : ind = 0 cur = arr while ind < len(self.limits): - cur = arr[position[ind]] + cur = cur[position[ind]] ind+=1 return cur + + def _add_to_element(self , val:int,position:tuple , arr:list): ind =0 - cur = arr - while ind < len(self.limits): - cur = arr[position[ind]] + cur = self.tree + while ind +1< len(self.limits): + cur = cur[position[ind]] ind+=1 - cur += val + cur[position[ind]] = cur[position[ind]] +val + - def add(self , position:tuple , val:int , ind:int =0 ): + def add(self , position:tuple , val:int , ind:int =0 , curPosition = []): if ind == len(self.limits): - self._add_to_element(val, tuple(self.backtrack_ind), self.tree) + newPosition = [i-1 for i in curPosition] + self._add_to_element(val, tuple(newPosition), self.tree) return - i = position[ind] =1 - while i < self.limits[ind] : - - self.backtrack_ind.append(i) - self.add(position , val , ind+1 ) - self.backtrack_ind.pop() - - i -= i &(-i) + i = position[ind] +1 + while i <= self.limits[ind] : + self.add(position , val , ind+1 , curPosition +[i]) + i += i &(-i) - def _get_sum_to_origin(self , position:tuple , ind :int ): + def _get_sum_to_origin(self , position:tuple , ind =0 , curPosition = [] ): res =0 if ind == len(self.limits): - res = self.get_sum(tuple(self.backtrack_ind)) + newPosition = [(i-1) for i in curPosition] + res = self._get_element(tuple(newPosition) ,self.tree) else : i = position[ind]+1 while i>0 : - self.backtrack_ind.append(i) - res += self.get_sum(position , ind+1 ) - self.backtrack_ind.pop() + res += self._get_sum_to_origin(position , ind+1 , curPosition+ [i]) i-= i& (-i) return res - def get_sum(self): - pass + def _get_sum(self , start:tuple , end:tuple ,ind:int , numberOfElements:int , resPoint :list , sign:int ): + if ind == len(start) : + if numberOfElements != 0 : + return 0 + res = self._get_sum_to_origin(tuple(resPoint)) + print(resPoint) + return sign *(res) + else : + res =0 + if numberOfElements -1 >=0 : + res += self._get_sum(start ,end , ind+1 , numberOfElements -1 , resPoint +[end[ind] -1 ], sign ) + res += self._get_sum(start ,end , ind+1 , numberOfElements , resPoint+[start[ind] ] ,sign ) + return res + def get_sum(self , start:tuple , end:tuple ): + res =0 + for i in range( 1 + len(self.limits)): + res += self._get_sum(start , end , 0 , i , [] , (-1)**i ) + return res + + + diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 0a0117841..d9797c619 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -725,20 +725,15 @@ def sumRect(sample ,i , j , l ,k ): assert sumRect(sample , l , k , i , j ) == bit2d.get_area(i, j , l ,k ) , message - - def test_BinaryIndexedTreeNd(): n = random.randint(2, 3) m = random.randint(2, 3) - k = random.randint(2, 3) + arr =[] for i in range(n): level1 = [] for j in range(m): - level2 = [] - for l in range(k): - level2.append(random.randint(5 ,12)) - level1.append(level2) + level1.append(random.randint(5 ,12)) arr.append(level1) bits = BinaryIndexedTreeNd(arr) - + From 3280d2858eba9d04f2e1e2477ec007a39257fbb7 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khaled Date: Thu, 17 Mar 2022 20:58:06 +0200 Subject: [PATCH 8/8] Add tests and descriptions --- AUTHORS | 1 + pydatastructs/trees/binary_trees.py | 56 +++++++++++++-- .../trees/tests/test_binary_trees.py | 72 ++++++++++++++++--- 3 files changed, 115 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index 566df712e..db51a0172 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,3 +9,4 @@ Prashant Rawat Harsheet Pratik Goyal Jay Thorat +Abdelraman Khaled Fouad diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 56c532d64..f5cf59921 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1863,9 +1863,30 @@ def get_area(self, start_x ,start_y ,end_x , end_y ): class BinaryIndexedTreeNd(object): """ - testing + ND Fenwick tree aka Binary Indexed Tree (BIT) + + Parameters + ============ + array :list + array of any dimintions. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples + ============= + >>> from pydatastructs import BinaryIndexedTreeNd + >>> bit = BinaryIndexedTree([[1, 2, 3] ,\ + [2 , 3 ,4 ] ,\ + [8 ,6 ,7 ]]) + >>> bit.get_sum((1, 1) , (0 ,0) ) + 8 + >>> bit.add((1 ,1) , 3) + >>> bit.get_sum((1, 1) , (0 , 0) ) + 11 + """ - __slots__ = ['tree' , 'array' , 'limits' , 'backtrack_ind'] + __slots__ = ['tree' , 'array' , 'limits' ] def __new__(cls, array, **kwargs): raise_if_backend_is_not_python(cls , kwargs.get('backend' , Backend.PYTHON)) @@ -1875,7 +1896,6 @@ def __new__(cls, array, **kwargs): while type(current_dimension)==list or type(current_dimension)==tuple : obj.limits.append(len(current_dimension)) current_dimension = current_dimension[0] - obj.backtrack_ind =[] obj.array =[] obj.array = obj._fillNdArray(0 , 0 , array) @@ -1931,6 +1951,21 @@ def _add_to_element(self , val:int,position:tuple , arr:list): def add(self , position:tuple , val:int , ind:int =0 , curPosition = []): + """ + + Parameters + ========== + position : tuple + position of value you want to add to. + + val :int + value you want to add. + + Returns + =========== + None + + """ if ind == len(self.limits): newPosition = [i-1 for i in curPosition] self._add_to_element(val, tuple(newPosition), self.tree) @@ -1958,7 +1993,6 @@ def _get_sum(self , start:tuple , end:tuple ,ind:int , numberOfElements:int , re if numberOfElements != 0 : return 0 res = self._get_sum_to_origin(tuple(resPoint)) - print(resPoint) return sign *(res) else : res =0 @@ -1967,6 +2001,20 @@ def _get_sum(self , start:tuple , end:tuple ,ind:int , numberOfElements:int , re res += self._get_sum(start ,end , ind+1 , numberOfElements , resPoint+[start[ind] ] ,sign ) return res def get_sum(self , start:tuple , end:tuple ): + """ + + Parameters + ---------- + start:tuple + point near orignal point. + end : tuple + point far from orignal point. + Returns + ------- + sum :int + sum of element between two points. + + """ res =0 for i in range( 1 + len(self.limits)): res += self._get_sum(start , end , 0 , i , [] , (-1)**i ) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index d9797c619..31564ca93 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -726,14 +726,66 @@ def sumRect(sample ,i , j , l ,k ): def test_BinaryIndexedTreeNd(): - n = random.randint(2, 3) - m = random.randint(2, 3) - - arr =[] - for i in range(n): - level1 = [] - for j in range(m): - level1.append(random.randint(5 ,12)) - arr.append(level1) + size = random.randint(1 , 4 ) + limits = [] + for i in range(size): + limits.append(random.randint(2 ,4 )) + + def makeArr(ind = 0 ): + if ind == size : + return random.randint(0 ,12) + else : + level = [] + for i in range(limits[ind]): + level.append(makeArr(ind +1 )) + return level + + arr =makeArr() bits = BinaryIndexedTreeNd(arr) - + assert arr == bits.array + + def getItem(ind , postion:tuple ): + ind = 0 + cur = arr + while ind <= len(postion) -1 : + cur = cur[postion[ind]] + ind+=1 + return cur + + def sum(ind , start:tuple , end:tuple ,position =[]): + if ind == size: + return getItem(0 , tuple(position)) + res =0 + for i in range(end[ind] , start[ind] +1 ): + res += sum(ind +1 , start , end , position +[i]) + return res + + def traverse(ind = 0 , position = []): + if ind == size : + f = lambda x : random.randint(0 , x ) + end = [f(position[i]) for i in range(size)] + message = f"{tuple(position)} and {tuple(end)}" + assert bits.get_sum(tuple(position) , tuple(end) ) == sum(0 , tuple(position) , tuple(end)) , message + assert bits.get_sum(tuple(position) , tuple(position)) == getItem(0 , tuple(position)) + else: + for i in range(limits[ind]): + traverse(ind+1 , position + [i]) + traverse() + def add(ind , position:tuple , val): + ind =0 + cur = arr + while ind +1 < size : + cur = cur[position[ind]] + ind +=1 + cur[position[ind]] += val + + for _ in range(0 ,10 ): + position =[] + for i in range(size): + position.append(random.randint(0, limits[i]-1 )) + bits.add(position , 10 ) + add(0 ,position , 10) + + traverse() + +