代码之家  ›  专栏  ›  技术社区  ›  fbrereto

矩阵“之字形”重新排序

  •  35
  • fbrereto  · 技术社区  · 14 年前

    我在MATLAB中有一个NxM矩阵,我想以类似于JPEG对子块像素重新排序的方式重新排序:

    zigzag layout pattern (image from Wikipedia)

    我希望算法是通用的,这样我就可以通过一个二维矩阵与任何维度。我是一个C++程序员,很想写一个旧的学校循环来完成这个任务,但是我怀疑在MATLAB中有更好的方法来完成它。

    我更希望有一个算法能在 NxN

    例子:

    1 2 3
    4 5 6  -->  1 2 4 7 5 3 6 8 9
    7 8 9
    
    6 回复  |  直到 6 年前
        1
  •  26
  •   Amro    14 年前

    考虑代码:

    M = randi(100, [3 4]);                      %# input matrix
    
    ind = reshape(1:numel(M), size(M));         %# indices of elements
    ind = fliplr( spdiags( fliplr(ind) ) );     %# get the anti-diagonals
    ind(:,1:2:end) = flipud( ind(:,1:2:end) );  %# reverse order of odd columns
    ind(ind==0) = [];                           %# keep non-zero indices
    
    M(ind)                                      %# get elements in zigzag order
    

    4x4矩阵示例:

    » M
    M =
        17    35    26    96
        12    59    51    55
        50    23    70    14
        96    76    90    15
    
    » M(ind)
    ans =
        17  35  12  50  59  26  96  51  23  96  76  70  55  14  90  15
    

    还有一个非平方矩阵的例子:

    M =
        69     9    16   100
        75    23    83     8
        46    92    54    45
    ans =
        69     9    75    46    23    16   100    83    92    54     8    45
    
        2
  •  9
  •   Community Stefan Steinegger    7 年前

    这种方法非常快:

    X = randn(500,2000); %// example input matrix
    [r, c] = size(X);
    M = bsxfun(@plus, (1:r).', 0:c-1);
    M = M + bsxfun(@times, (1:r).'/(r+c), (-1).^M);
    [~, ind] = sort(M(:));
    y = X(ind).'; %'// output row vector
    

    下面的代码将运行时间与 Amro's excellent answer ,使用 timeit . 它测试矩阵大小(条目数)和矩阵形状(行数与列数之比)的不同组合。

    %// Amro's approach
    function y = zigzag_Amro(M)
    ind = reshape(1:numel(M), size(M));
    ind = fliplr( spdiags( fliplr(ind) ) );     
    ind(:,1:2:end) = flipud( ind(:,1:2:end) );
    ind(ind==0) = [];
    y = M(ind);
    
    %// Luis' approach
    function y = zigzag_Luis(X)
    [r, c] = size(X);
    M = bsxfun(@plus, (1:r).', 0:c-1);
    M = M + bsxfun(@times, (1:r).'/(r+c), (-1).^M);
    [~, ind] = sort(M(:));
    y = X(ind).';
    
    %// Benchmarking code:
    S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
    f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
    %// f = [0.5 2]; %// plotted with '--'
    %// f = [2 0.5]; %// plotted with ':'
    t_Amro = NaN(size(S));
    t_Luis = NaN(size(S));
    for n = 1:numel(S)
        X = rand(f(1)*S(n), f(2)*S(n));
        f_Amro = @() zigzag_Amro(X);
        f_Luis = @() zigzag_Luis(X);
        t_Amro(n) = timeit(f_Amro);
        t_Luis(n) = timeit(f_Luis);
    end
    loglog(S.^2*prod(f), t_Amro, '.b-');
    hold on
    loglog(S.^2*prod(f), t_Luis, '.r-');
    xlabel('number of matrix entries')
    ylabel('time')
    

    下图是在Windows7 64位上使用MatlabR2014B获得的。R2010b的结果非常相似。可以看出,新方法将运行时间缩短了2.5倍(对于小矩阵)到1.4倍(对于大矩阵)。结果被认为是几乎不敏感的矩阵形状,给定的条目总数。

    enter image description here

        3
  •  8
  •   gnovice    14 年前

    zig_zag.m . 它看起来很难看,但很管用!:

    function [M,index] = zig_zag(M)
      [r,c] = size(M);
      checker = rem(hankel(1:r,r-1+(1:c)),2);
      [rEven,cEven] = find(checker);
      [cOdd,rOdd] = find(~checker.'); %'#
      rTotal = [rEven; rOdd];
      cTotal = [cEven; cOdd];
      [junk,sortIndex] = sort(rTotal+cTotal);
      rSort = rTotal(sortIndex);
      cSort = cTotal(sortIndex);
      index = sub2ind([r c],rSort,cSort);
      M = M(index);
    end
    

    以及测试矩阵:

    >> M = [magic(4) zeros(4,1)];
    
    M =
    
        16     2     3    13     0
         5    11    10     8     0
         9     7     6    12     0
         4    14    15     1     0
    
    >> newM = zig_zag(M)    %# Zig-zag sampled elements
    
    newM =
    
        16
         2
         5
         9
        11
         3
        13
        10
         7
         4
        14
         6
         8
         0
         0
        12
        15
         1
         0
         0
    
        4
  •  5
  •   Jonas    14 年前

    这里有一个方法。基本上,你的数组是一个hankel矩阵加上1:m的向量,其中m是每个对角线上的元素数。也许其他人对如何创建对角线数组有很好的想法,这些对角线数组必须添加到翻转的hankel数组中,而不需要循环。

    我认为这应该可以推广到非正方形数组。

    % for a 3x3 array 
    n=3;
    
    numElementsPerDiagonal = [1:n,n-1:-1:1];
    hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
    array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
    
    % loop through the hankel array and add numbers counting either up or down
    % if they are even or odd
    for d = 1:(2*n-1)
       if floor(d/2)==d/2
          % even, count down
          array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
       else
          % odd, count up
          array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
       end
    end
    
    % now flip to get the result
    indexMatrix = fliplr(array2add)
    
    result =
         1     2     6
         3     5     7
         4     8     9
    

    之后,你只要打电话 reshape(image(indexMatrix),[],1)

    编辑

    sort 就像马克建议的那样。

    indexMatrixT = indexMatrix';   % ' SO formatting
    [dummy,sortedIdx] = sort(indexMatrixT(:));
    
    sortedIdx =
         1     2     4     7     5     3     6     8     9
    

        5
  •  4
  •   Community Stefan Steinegger    7 年前

    X 作为输入2D矩阵 square landscape-shaped ,这似乎是相当有效的-

    [m,n] = size(X);
    nlim = m*n;
    n = n+mod(n-m,2);
    mask = bsxfun(@le,[1:m]',[n:-1:1]);
    start_vec = m:m-1:m*(m-1)+1;
    a = bsxfun(@plus,start_vec',[0:n-1]*m);
    offset_startcol = 2- mod(m+1,2);
    [~,idx] = min(mask,[],1);
    idx = idx - 1;
    idx(idx==0) = m;
    end_ind = a([0:n-1]*m + idx);
    
    offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
    a(:,offset_startcol:2:end) = bsxfun(@minus,offsets,a(:,offset_startcol:2:end));
    out = a(mask);
    out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
    result = X([out2 ; out(out<=nlim)]);
    

    快速运行时测试 Luis's approach -

    Datasize: 500 x 2000
    ------------------------------------- With Proposed Approach
    Elapsed time is 0.037145 seconds.
    ------------------------------------- With Luis Approach
    Elapsed time is 0.045900 seconds.
    
    
    Datasize: 5000 x 20000
    ------------------------------------- With Proposed Approach
    Elapsed time is 3.947325 seconds.
    ------------------------------------- With Luis Approach
    Elapsed time is 6.370463 seconds.
    
        6
  •  0
  •   Marc    14 年前

    [~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
    reorderedim = im(I);
    

    我没有看到一个明显的解决方案来生成idx而不使用for循环或递归,但我会考虑更多。