Shift rows in matrix with respect to vector values

2020-02-12 21:20发布

Can I shift rows in matrix A with respect to values in vector v?

For instance A and v specified as follows:

A =
    1   0   0
    1   0   0
    1   0   0

v =
    0   1   2

In this case I want to get this matrix from A:

A = 
    1   0   0
    0   1   0
    0   0   1

Every i-th row in A has been shifted by i-th value in v

Can I do this operation with native functions? Or should I write it by myself?

I've tried circshift function, but I couldn't figure out how to shift rows separately.

4条回答
家丑人穷心不美
2楼-- · 2020-02-12 21:48
% A and v as above. These could be function input arguments
A = [1 0 0; 1 0 0; 1 0 0]; 
v = [0 1 2];                                          
assert (all (size (v) == [1, size(A, 1)]), ...
        'v needs to be a horizontal vector with as many elements as rows of A');

% Calculate shifted indices
[r, c] = size (A);
tmp = mod (repmat (0 : c-1, r, 1) - repmat (v.', 1, c), c) + 1;
Out = A(sub2ind ([r, c], repmat ([1 : r].', 1, c), tmp))  

  Out =

       1     0     0
       0     1     0
       0     0     1

If performance is an issue, you can replace repmat with an equivalent bsxfun call which is more efficient (I use repmat here for simplicity to demonstrate the approach).

查看更多
▲ chillily
3楼-- · 2020-02-12 21:53

The function circshift does not work as you want and even if you use a vector for the amount of shift, that is interpreted as the amount of shift for each dimension. While it is possible to loop over the rows of your matrix, that will not be very efficient.

More efficient is if you compute the indexing for each row which is actually quite simple:

## First, prepare all your input
octave> A = randi (9, 4, 6)
A =

   8   3   2   7   4   5
   4   4   7   3   9   1
   1   6   3   9   2   3
   7   4   1   9   5   5

octave> v = [0 2 0 1];
octave> sz = size (A);


## Compute how much shift per row, the column index (this will not work in Matlab)
octave> c_idx = mod ((0:(sz(2) -1)) .- v(:), sz(2)) +1
c_idx =

   1   2   3   4   5   6
   5   6   1   2   3   4
   1   2   3   4   5   6
   6   1   2   3   4   5

## Convert it to linear index    
octave> idx = sub2ind (sz, repmat ((1:sz(1))(:), 1, sz(2)) , c_idx);

## All you need is to index
octave> A = A(idx)
A =

   8   3   2   7   4   5
   9   1   4   4   7   3
   1   6   3   9   2   3
   5   7   4   1   9   5
查看更多
兄弟一词,经得起流年.
4楼-- · 2020-02-12 21:56

Shift vector with circshift in loop, iterating row index.

查看更多
5楼-- · 2020-02-12 22:04

With focus on performance, here's one approach using bsxfun/broadcasting -

[m,n] = size(A);
idx0 = mod(bsxfun(@plus,n-v(:),1:n)-1,n);
out = A(bsxfun(@plus,(idx0*m),(1:m)'))

Sample run -

A =
     1     7     5     7     7
     4     8     5     7     6
     4     2     6     3     2
v =
     3     1     2
out =
     5     7     7     1     7
     6     4     8     5     7
     3     2     4     2     6

Equivalent Octave version to use automatic broadcasting would look something like this -

[m,n] = size(A);
idx0 = mod( ((n-v(:)) + (1:n)) -1 ,n);
out = A((idx0*m)+(1:m)')
查看更多
登录 后发表回答