【问题标题】:Extract matrix elements提取矩阵元素
【发布时间】:2012-08-24 21:53:03
【问题描述】:

如果我有一个矩阵:

0   0   3   4  
4   3   2   0  
2   0   2   0  

我想将非零元素提取到一些批次中,关于一个规则:如果一个元素已经被取走,那么同一行/列中的另一个元素是不允许的。所以提取的矩阵将是:
第一批:

0   0   3   0  
4   0   0   0  
0   0   0   0  

第二批:

0   0   0   4  
0   3   0   0  
0   0   2   0  

第三批:

0   0   0   0  
0   0   2   0  
2   0   0   0  

也接受任何其他批次组合,只要涵盖所有非零元素并且符合规则。你会如何在 MATLAB/Octave 中做到这一点?

【问题讨论】:

    标签: matlab octave


    【解决方案1】:

    对于行,我会这样做:

    A = [ 0   0   3   4  ;
          4   3   2   0  ;
          2   0   2   0 ];
    
    1. 检查数字是否非零:

      Anonzero=A~=0;
      >> Anonzero
           0     0     1     1
           1     1     1     0
           1     0     1     0
      
    2. 沿着Anonzero的行走cumsum

      Aidx=cumsum(A,[],2);
      >> Aidx
           0     0     1     2
           1     2     3     3
           1     1     2     2
      
      numbatches=max(Aidx(:,end));
      
    3. 将零值的索引设置回零,这样它们就不会被选中

      A(~Anonzero)=0;
      
    4. 提取批次:

      batch=cell(numbatches,1);
      for ii=1:numbatches
          batch{ii}=A.*(Aidx==ii);
      end
      

    导致:

    >>batch{1}
        0     0     3     0
        4     0     0     0
        2     0     0     0
    
    >>batch{2}
        0     0     0     4
        0     3     0     0
        0     0     2     0
    
    >>batch{3}
        0     0     0     0
        0     0     2     0
        0     0     0     0
    

    我认为对于行 列规则可以做类似的事情,但我没有马上看到它。我会考虑的 ;)

    【讨论】:

      【解决方案2】:

      冈瑟已经走在正确的轨道上。你想选择一个元素,如果

      1. 非零的行累积和为 1 AND
      2. 非零的列cumsum是1 AND
      3. 元素本身不为零。

      下面的代码解决了这个问题:

      A = [0, 0, 3, 4;
           4, 3, 2, 0;
           2, 0, 2, 0];
      
      batches = cell(0);
      while any(A(:)~=0)
          selector = cumsum(A~=0, 1) .* cumsum(A~=0, 2) .* (A~=0) == 1;
          batches{end+1} = A .* selector;
          A(selector) = 0;
      end
      

      但是请注意,返回的解决方案不是最优的,因为它的第二批是

      0   0   0   4  
      0   3   0   0  
      2   0   0   0 
      

      这意味着剩余的矩阵元素来自同一列:

      0   0   0   0
      0   0   2   0  
      0   0   2   0 
      

      很遗憾,您不能在同一批次中绘制它们。所以你最终得到了四批而不是三批。

      编辑: 或许,首先选择那些出现在行/列中且有很多非零的元素是个好主意。例如,可以使用这些权重

      weight = repmat(sum(A~=0, 1), size(A, 1), 1) ...
               .* repmat(sum(A~=0, 2), 1, size(A, 2)) .* (A~=0)
      
      weight =
           0     0     6     2
           6     3     9     0
           4     0     6     0
      

      以下算法

      batches = cell(0);
      while any(A(:)~=0)
          batch = zeros(size(A));
          weight = repmat(sum(A~=0, 1), size(A, 1), 1) ...
                   .* repmat(sum(A~=0, 2), 1, size(A, 2)) .* (A~=0);
          while any(weight(:)~=0)
              [r,c] = find(weight == max(weight(:)), 1);
              batch(r,c) = A(r,c);
              A(r,c) = 0;
              weight(r,:) = 0;
              weight(:,c) = 0;
          end
          batches{end+1} = batch;
      end
      

      返回这些批次。

      batches{:}
      ans =
           0     0     0     4
           0     0     2     0
           2     0     0     0
      
      ans =
           0     0     3     0
           4     0     0     0
           0     0     0     0
      
      ans =
           0     0     0     0
           0     3     0     0
           0     0     2     0
      

      所以它至少对这个小测试用例有效。

      【讨论】:

      • 现在这个循环看起来非常熟悉......:)
      • @Rody:是的。感谢你的算法结构和我的权重;-)
      • 顺便说一句,我认为将 repmats 相加而不是将它们相乘可能会更好。
      • 感谢您的解决方案,感谢大家的有趣讨论。这超出了我的预期...... :)
      【解决方案3】:

      无疑是一个有趣的问题...我猜@GuntherStruyf 的方法最终将是您应该选择的方法。但是,这是一个使用循环的简单解决方案:

      A = [
          0   0   3   4
          4   3   2   0
          2   0   2   0  ];
      
      C = {};
      nz = A ~= 0;
      while any(nz(:))
      
          tmpNz = nz;
          tmpA  = A;
          newNz = false(size(nz));    
          while true
      
              [i,j] = find(tmpNz, 1);
              if isempty(i) || isempty(j), break; end
      
              tmpNz(i,:) = false;
              tmpNz(:,j) = false;
              newNz(i,j) = true;
      
          end
      
          tmpA(~newNz) = false;
          C{end+1} = tmpA;
          nz(newNz) = false;
      
      end
      

      一旦你摆脱了不断增长的单元阵列,这应该会非常快,例如,通过预先分配大量初始元素,然后删除未使用的元素。

      不过,我会等到@GuntherStruyf 弄清楚他的事情!

      【讨论】:

      • 我今天/下周末/下周没有太多空闲时间,所以请随意扩展我的解决方案:p
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-14
      • 2018-09-23
      • 1970-01-01
      • 2021-04-28
      • 2020-12-24
      相关资源
      最近更新 更多