寻找矩阵子矩阵的最有效方法[matlab]

时间:2018-04-16 20:17:16

标签: algorithm matlab submatrix

假设我们有一个零和一个矩阵

 0     1     1     1     0     0     0
 1     1     1     1     0     1     1
 0     0     1     0     0     1     0
 0     1     1     0     1     1     1
 0     0     0     0     0     0     1
 0     0     0     0     0     0     1

我们希望找到所有子矩阵(我们只需要角的行索引和列索引)以及这些属性:

  1. 至少包含L个和L个零
  2. 包含最多H个元素
  3. 即。取前面的矩阵,L = 1,H = 5,子矩阵1 2 1 4(行索引1 2和列索引1 4)

     0     1     1     1
     1     1     1     1
    

    满足属性1但有8个元素(大于5)所以它不好;

    矩阵4 5 1 2

     0     1
     0     0
    

    很好,因为满足了这两个属性。

    目标是找到最小面积为2 * L,最大面积为H并且至少包含L个和L个零的所有子矩阵。

    如果我们将矩阵视为矩形,通过查看从H到2 * L的所有数字的除数,很容易找到最大面积H和最小面积2 * L的所有可能子矩形。

    例如,当H = 5且L = 1时,所有可能的子矩形/子矩阵由

    的除数给出
    • H = 5 - >除数[1 5] - >区域5的可能矩形是1x5和5x1
    • 4 - >除数[1 2 4] - >区域4的可能矩形是1x4 4x1和2x2
    • 3 - >除数[1 3] - >区域3的可能矩形是3x1和1x3
    • 2 * L = 2 - >除数[1 2] - >区域2的可能矩形是2x1和1x2

    我写了这段代码,对于每个数字找到它的除数并在它们上面循环以找到子矩阵。为了找到子矩阵,它会这样做:例如1x5子矩阵,代码的作用是固定矩阵的第一行并逐步(沿着矩阵的所有列)从左边缘的子矩阵移动矩阵到矩阵的右边缘,然后代码修复矩阵的第二行,并从左到右沿着所有列移动子矩阵,依此类推,直到它到达最后一行。

    它对所有1x5子矩阵执行此操作,然后它考虑5x1子矩阵,然后是1x4,然后是4x1,然后是2x2等。

    代码在2秒内完成工作(它找到所有子矩阵)但是对于大矩阵,即200x200,需要很多分钟才能找到所有子矩阵。所以我想知道是否有更有效的方法来完成这项工作,最终哪种方式最有效。

    这是我的代码:

    clc;clear all;close all
    
    %% INPUT
    P=  [0     1     1     1     0     0     0 ;
         1     1     1     1     0     1     1 ;
         0     0     1     0     0     1     0 ;
         0     1     1     0     1     1     1 ;
         0     0     0     0     0     0     1 ;
         0     0     0     0     0     0     1];
    L=1; % a submatrix has to containg at least L ones and L zeros
    H=5; % max area of a submatrix
    
    [R,C]=size(P); % rows and columns of P
    sub=zeros(1,6); % initializing the matrix containing the indexes of each submatrix (columns 1-4), their area (5) and the counter (6)
    counter=1; % no. of submatrices found
    
    %% FIND ALL RECTANGLES OF AREA >= 2*L & <= H
    %
    % idea: all rectangles of a certain area can be found using the area's divisors
    %       e.g. divisors(6)=[1 2 3 6] -> rectangles: 1x6 6x1 2x3 and 3x2
    tic
    for sH = H:-1:2*L % find rectangles of area H, H-1, ..., 2*L
        div_sH=divisors(sH); % find all divisors of sH
        disp(['_______AREA ', num2str(sH), '_______'])
    
        for i = 1:round(length(div_sH)/2) % cycle over all couples of divisors        
            div_small=div_sH(i);
            div_big=div_sH(end-i+1);        
    
            if div_small <= R && div_big <= C % rectangle with long side <= C and short side <= R
    
                for j = 1:R-div_small+1 % cycle over all possible rows
    
                    for k = 1:C-div_big+1 % cycle over all possible columns                    
                        no_of_ones=length(find(P(j:j-1+div_small,k:k-1+div_big))); % no. of ones in the current submatrix
    
                        if  no_of_ones >= L  &&  no_of_ones <= sH-L % if the submatrix contains at least L ones AND L zeros                        
                            %               row indexes    columns indexes         area         position
                            sub(counter,:)=[j,j-1+div_small , k,k-1+div_big , div_small*div_big , counter]; % save the submatrix
                            counter=counter+1;
                        end
                    end
                end
                disp([' [', num2str(div_small), 'x', num2str(div_big), '] submatrices: ', num2str(size(sub,1))])
            end
            if div_small~=div_big % if the submatrix is a square, skip this part (otherwise there will be duplicates in sub)
    
                if div_small <= C && div_big <= R % rectangle with long side <= R and short side <= C
    
                    for j = 1:C-div_small+1 % cycle over all possible columns
    
                        for k = 1:R-div_big+1 % cycle over all possible rows                        
                            no_of_ones=length(find(P(k:k-1+div_big,j:j-1+div_small)));
    
                            if no_of_ones >= L  &&  no_of_ones <= sH-L
                                sub(counter,:)=[k,k-1+div_big,j,j-1+div_small , div_big*div_small, counter];
                                counter=counter+1;
                            end
                        end
                    end
                    disp([' [', num2str(div_big), 'x', num2str(div_small), '] submatrices: ', num2str(size(sub,1))])
                end
            end
        end
    end
    fprintf('\ntime: %2.2fs\n\n',toc)
    

2 个答案:

答案 0 :(得分:3)

这是一个以2D matrix convolution.为中心的解决方案。粗略的想法是将每个子矩阵形状的.java:476: error: ';' expected: if(changeState.getCounter().equals(2)){与第二个矩阵卷积,使得得到的矩阵的每个元素表示子矩阵中有多少个它位于所述元素的左上角。像这样,您可以一次性获得单个形状的所有解决方案,而无需在行/列上循环,大大加快了速度(在我8岁的笔记本电脑上,200x200矩阵只需不到一秒钟)

P

答案 1 :(得分:1)

首先,我建议您结合查找允许的子矩阵大小。

for smaller = 1:sqrt(H)
    for larger = 2*L:H/smaller
        # add smaller X larger and larger x smaller to your shapes list

接下来,从形状中的最小矩形开始。请注意,对小矩形的任何解决方案都可以在任何方向上扩展到H的区域限制,并且添加的元素不会使您找到的解决方案无效。这将确定许多解决方案,而无需检查其中的人口。

跟踪您找到的解决方案。当您朝着更大的矩形方向前进时,您可以避免检查solutions集中已有的任何内容。如果将其保留在哈希表中,则检查成员资格为 O(1)。之后您需要检查的是大多数块,大多数为1,与大多数为0相邻。这应该会加快处理速度。

这足以帮助吗?