Octave/MATLAB中根据向量值对矩阵行进行移位

3

我能否根据向量 v 中的值,移动矩阵 A 中的行?

例如,给定以下的矩阵 A 和向量 v

A =
    1   0   0
    1   0   0
    1   0   0

v =
    0   1   2

在这种情况下,我希望从A中获取此矩阵:
A = 
    1   0   0
    0   1   0
    0   0   1

A中的每一行都被v中相应位置的值向右移动了i个单位。

我能否使用原生函数来执行此操作?还是我需要自己编写?

我尝试过circshift函数,但我无法分别移动每一行。

4个回答

4
< p > 函数circshift不能按照您的要求工作,即使您使用一个向量来进行移位,也会被解释为每个维度的移位量。虽然可以循环处理矩阵的行,但这样做效率不高。

更有效的方法是计算每一行的索引,这实际上非常简单:

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

1
我认为Matlab在最新版本中刚刚引入了广播功能。 :) - Tasos Papastylianou
最好使用 mod 而不仅仅添加偏移量,因为可以合理地假设 v 可能包含负数 "shifts"。 - Tasos Papastylianou
@TasosPapastylianou,你是认真要在Matlab中广播吗?请提供链接。你关于mod的说法是正确的,它支持负值并且看起来更好。这样它就和你的答案一样了。 - carandraug
1
也许是Julia,但自我记忆以来Octave就在脚本和命令行中支持函数定义。我不认为他们在抄袭其他语言,他们只是在赶上其他语言的步伐。什么样的现代语言不支持脚本文件中的函数?只有Matlab是那么落后的。 - carandraug
4
我认为在这里使用“智障”这个词太过强烈。在其他方面,Octave比Matlab落后,但我从未想过称它为“智障” :-) - Luis Mendo
显示剩余3条评论

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

如果性能是一个问题,你可以使用更高效的等价 bsxfun 调用来替换 repmat (我在这里使用 repmat 是为了简单起见,以展示这种方法)。


如果行数和列数不匹配,那么您对 sub2ind 的调用是错误的。例如,尝试使用 A = randi(9, 3, 6) 进行测试。 - carandraug

1

关注性能,这里介绍一种使用bsxfun/broadcasting的方法 -

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

样例运行 -

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

使用自动广播的等效Octave版本如下所示 -

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

0

使用循环和circshift来移动向量,迭代行索引。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接