从向量创建非方对角矩阵

Creating a non-square diagonal matrix from a vector

我有一个向量,我想让它向前重复 n 次,向后重复 n 次,但对角线。

例如,我有向量:
x= [0 0 1 1 0 0],

并且想要一个大小为 6x5 的矩阵,如下所示:

1 0 0 0 0
1 1 0 0 0
0 1 1 0 0
0 0 1 1 0
0 0 0 1 1
0 0 0 0 1

这意味着向量[0 0 1 1 0 0](转置)放在中间,我想要一个矩阵,这样当向左移动时,元素循环移动到顶部每次移动你向左。类似地,当向右移动时,元素会循环向下移动到底部,因为您每次向右移动。因此,第二列将是 [0 1 1 0 0 0] ,其中元素以循环方式向左移动一次,然后第一列将是 [1 1 0 0 0 0] ,我们将所有元素相对于中间和一次关于第二列。

同样,第四列是[0 0 0 1 1 0],意思是相对于中间一列,我们将所有元素以循环的方式向右移动一次,那么最后一列就是[0 0 0 0 1 1]我们将所有元素相对于中间向右移动两次,相对于第四列向右移动一次。

如果向量总是由中间的非零部分组成,您可以使用 convmtx(来自信号处理工具箱),如下所示:

y = convmtx(nonzeros(x), numel(x)-1);

或者,如果您没有信号处理工具箱,请使用 conv2:

y = conv2(eye(numel(x)-1), nonzeros(x)):

对于 x = [0 0 1 1 0 0],以上任一项都会产生:

y =
     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1

convmtx 需要信号处理工具箱才能计算结果。虽然 Luis 的回答很好,但我可以推荐一种不依赖工具箱的方法吗?

n = 2;
ind = mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1;
y = x(ind);

或者,如果您不想要中间变量:

n = 2;
y = x(mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1);

对于 x = [0 0 1 1 0 0];,我们得到:

y =

     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1

这段代码的解释很简单。 n 表示您希望 "repeat" 向量 x 向左和向右移动的次数,其中每列循环向上或向下移动元素,具体取决于方向你领导的矩阵。

第二行代码最让人望而生畏。让我们从 bsxfun(...) 调用开始:

bsxfun(@plus, (0:numel(x)-1).', n:-1:-n))

这将创建一个 numel(x) x (2*n + 1) 矩阵,其中每一列都是简单的向量 (0:numel(x)-1),但添加了一个常数值。从第一列开始,我们将 n 添加到 (0:numel(x)-1),然后在第二列我们将 n-1 添加到 (0:numel(x)-1) 直到我们到达中间即 (0:numel(x)-1) 通过它自己。在你通过中间之后,我们然后 减去 带有常数的向量,从 -1n+1 列,-2n+2 列直到结束。 n = 2 得到的结果是:

ans =

     2     1     0    -1    -2
     3     2     1     0    -1
     4     3     2     1     0
     5     4     3     2     1
     6     5     4     3     2
     7     6     5     4     3

在理想情况下,我们基本上会使用这个矩阵来索引我们的向量,以获得我们想要的矩阵。中间左侧的列通过指定前进 1 的索引来逐步访问元素,结果是将元素移向矩阵的顶部。类似地,通过指定延迟 1 的索引,中间渐进访问元素右侧的列,结果是将元素移向矩阵的底部。

不幸的是,我们有负值和超过向量长度的值。最重要的是,MATLAB 从 1 开始索引。因此,您将不得不使用一些环绕逻辑来确保一旦我们超过向量的长度或为索引产生负值,我们应该环绕到 1 而不是 0 或者向量的长度而不是 -1。因此,我们可以简单地放置一个 mod(模数/余数)运算,以 x 中的元素总数为界,然后在将 1 加到整个矩阵之后,这样我们就可以将索引绑定在 1 和现在将我们带到完整的第二行代码的元素总数:

>> ind = mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1

ind =

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

最后一步是简单地使用此矩阵索引到您的向量中以获得所需的输出矩阵:

>> y = x(ind)

y =

     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1