Largest rectangular sub-matrix with the same number

I am trying to come up with a dynamic programming algorithm that finds the largest submatrix in a matrix consisting of the same number:

Example:

{5 5 8} {5 5 7} {3 4 1} 

Answer: 4 elements due to matrix

  5 5 5 5 
+14
algorithm matrix dynamic-programming
Oct 14 '11 at 16:55
source share
4 answers

This is a question that I already answered here (and here , a modified version). In both cases, the algorithm was applied to the binary case (zeros and ones), but the modification for arbitrary numbers is quite simple (but, unfortunately, I save the images for the binary version of the problem ). You can do this very efficiently using the two- process linear O(n) time algorithm - n is the number of elements. However, this is not dynamic programming — I think that using dynamic programming here would be awkward and inefficient, after all, because of the difficulties with the decomposition problem, as mentioned in the OP, unless it’s homework - but in this case you You can try to impress with this algorithm :-), because, obviously, there is no faster solution than O(n) .

Algorithm (images depict binary case) :

Suppose you want to find the largest rectangle of free (white) elements.

enter image description here

Here follows an algorithm for passing linear O(n) time (n is the number of elements):

1) in the first pass , go through the columns, from bottom to top, and for each element, indicate the number of consecutive elements available before that:

enter image description here

repeat until:

enter image description here

Images depict binary case. In the case of arbitrary numbers, you hold 2 matrices - first with the original numbers, and the second with additional numbers, which are filled with the image above. You must check the original matrix, and if you find a number different from the previous one, you will start numbering again (in the auxiliary matrix) from 1.

2) in the second pass, you go through the lines, maintaining the data structure of potential rectangles, i.e. rectangles containing the current position somewhere on the top edge. See the following image (current position of red, 3 potential rectangles - purple - height 1, green - height 2 and yellow - height 3):

enter image description here

For each rectangle, we keep its height k and its left edge. In other words, we keep track of the sums of consecutive numbers that were >= k (i.e., Potential rectangles with height k ). This data structure can be represented by an array with a double linked list linking busy elements, and the size of the array will be limited by the height of the matrix.

Pseudo-code of the second pass (not a binary version with arbitrary numbers):

 var m[] // original matrix var aux[] // auxiliary matrix filled in the 1st pass var rect[] // array of potential rectangles, indexed by their height // the occupied items are also linked in double linked list, // ordered by height foreach row = 1..N // go by rows foreach col = 1..M if (col > 1 AND m[row, col] != m[row, col - 1]) // new number close_potential_rectangles_higher_than(0); // close all rectangles height = aux[row, col] // maximal height possible at current position if (!rect[height]) { // rectangle with height does not exist create rect[height] // open new rectangle if (rect[height].next) // rectangle with nearest higher height // if it exists, start from its left edge rect[height].left_col = rect[height].next.left_col else rect[height].left_col = col; } close_potential_rectangles_higher_than(height) end for // end row close_potential_rectangles_higher_than(0); // end of row -> close all rect., supposing col is M+1 now! end for // end matrix 

Rectangle close function:

 function close_potential_rectangles_higher_than(height) close_r = rectangle with highest height (last item in dll) while (close_r.height > height) { // higher? close it area = close_r.height * (col - close_r.left_col) if (area > max_area) { // we have maximal rectangle! max_area = area max_topleft = [row, close_r.left_col] max_bottomright = [row + height - 1, col - 1] } close_r = close_r.prev // remove the rectangle close_r from the double linked list } end function 

This way you can also get all max rectangles. So, in the end you will get:

enter image description here

And what will be the difficulty? You see that the close_potential_rectangles_higher_than function is O(1) for each closed rectangle. Since for each field we create 1 potential rectangle at the maximum, the total number of potential rectangles ever present on a particular line never exceeds the line length. Therefore, the complexity of this function O(1) amortized!

Thus, the complexity is O(n) , where n is the number of matrix elements.

+25
Oct 14 2018-11-21T00:
source share

Dynamic solution:

Define a new matrix A that will store two values ​​in A[i,j] : the width and height of the largest submatrix with the upper left corner in i,j , fill this matrix, starting from the lower right corner, rows from top to bottom. You will find four cases:

case 1: none of the elements of the right or lower neighbor in the source matrix is ​​equal to the current one, i.e. M[i,j] != M[i+1,j] and M[i,j] != M[i,j+1] is M original matrix, in this case the value A[i,j] is 1x1

case 2: the neighboring element on the right is equal to the current one, and the lower one is different, the value A[i,j].width is equal to A[i+1,j].width+1 and A[i,j].height=1

case 3: the neighboring element from below is equal, and the right one is different, A[i,j].width=1, A[i,j].height=A[i,j+1].height+1

case 4: both neighborhoods are equal: A[i,j].width = min(A[i+1,j].width+1,A[i,j+1].width) and A[i,j].height = min(A[i,j+1]+1,A[i+1,j])

the size of the largest matrix with the upper left corner in i,j is A[i,j].width*A[i,j].height , so you can update the maximum value found when calculating A[i,j]

the bottom row and the rightmost elements of the column are treated as if their neighbors below and to the right are respectively different

in your example, the resulting matrix A will be:

 {2:2 1:2 1:1} {2:1 1:1 1:1} {1:1 1:1 1:1} 

w:h width:height

+3
Oct. 14 2018-11-11T00:
source share

Modification of the above answer:

Define a new matrix A that stores two values ​​in [i, j]: the width and height of the largest submatrix with the upper left corner in i, j, fill this matrix, starting from the lower right corner, in rows from top to bottom. You will find four cases:

case 1: none of the elements of the right or lower neighbor in the source matrix is ​​equal to the current one, i.e. M [i, j]! = M [i + 1, j] and M [i, j]! = M [i, j + 1], which is the M source matrix, in this case the value A [i, j] is 1x1

case 2: the neighboring element on the right is equal to the current one, and the lower one is different, the value A [i, j] .width is equal to A [i + 1, j] .width + 1 and A [I, J] .height = 1

case 3: the neighboring element from below is equal, but the right one is different, A [i, j] .width = 1, A [i, j] .height = A [i, j + 1]. height + 1

case 4: both neighborhoods are equal: Three rectangles are considered: 1. A [i, j] .width = A [i, j + 1] .width + 1; A [I, J] .height = 1;

  • A [I, J] .height = A [I + 1, J] .height + 1; and [I, J] .width = 1;

  • A [i, j] .width = min (A [i + 1, j] .width + 1, A [i, j + 1] .width) and A [i, j] .height = min (A [i , j + 1] + 1, A [i + 1, j])

One that has a maximum area in the three above cases will be considered as representing a rectangle in that position.

The size of the largest matrix, which has the upper left corner in i, j, is A [i, j] .width * A [i, j] .height, so you can update the maximum value found by calculating A [I, J]

the bottom row and the rightmost elements of the column are treated as if their neighbors below and to the right were respectively different.

0
Jul 15 '14 at 5:01
source share

This question is a duplicate . I tried to label this as a duplicate. Here is a Python solution that also returns the position and shape of the largest rectangular submatrix:

 #!/usr/bin/env python3 import numpy s = '''5 5 8 5 5 7 3 4 1''' nrows = 3 ncols = 3 skip_not = 5 area_max = (0, []) a = numpy.fromstring(s, dtype=int, sep=' ').reshape(nrows, ncols) w = numpy.zeros(dtype=int, shape=a.shape) h = numpy.zeros(dtype=int, shape=a.shape) for r in range(nrows): for c in range(ncols): if not a[r][c] == skip_not: continue if r == 0: h[r][c] = 1 else: h[r][c] = h[r-1][c]+1 if c == 0: w[r][c] = 1 else: w[r][c] = w[r][c-1]+1 minw = w[r][c] for dh in range(h[r][c]): minw = min(minw, w[r-dh][c]) area = (dh+1)*minw if area > area_max[0]: area_max = (area, [(r, c, dh+1, minw)]) print('area', area_max[0]) for t in area_max[1]: print('coord and shape', t) 

Output:

 area 4 coord and shape (1, 1, 2, 2) 
0
May 24 '15 at
source share



All Articles