提取矩阵元素

时间:2012-08-24 08:03:20

标签: matlab octave

如果我有一个矩阵:

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  

第3批:

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

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

3 个答案:

答案 0 :(得分:3)

对于行,我会按如下方式执行:

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
    
  5. 导致:

    >>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
    

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

答案 1 :(得分:2)

毫无疑问,一个有趣的问题......我的猜测是@ 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把他的事情搞清楚了!

答案 2 :(得分:2)

冈瑟已经走上了正轨。您想要选择一个元素,如果

  1. 非零的行cumsum为1 AND
  2. 非零列的cumsum为1 AND
  3. 元素本身不为零。
  4. 以下代码解决了这个问题:

    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
    

    所以它至少适用于这个小测试用例。