【问题标题】:Creating a non-square diagonal matrix from a vector从向量创建非方形对角矩阵
【发布时间】:2016-08-25 16:31:35
【问题描述】:

我有一个向量,我想让它向前重复 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],我们将所有元素移动到右边两次相对于中间,一次相对于第四列。

【问题讨论】:

  • 看看toeplitz
  • 我看到了这里的模式,但是向前和向后重复 n 次是什么意思? spdiags 不工作吗?
  • 我应该提到,这些矩阵会非常大,大约 75000 x 201。我有一个包含 201 个 0 数据点的向量,其中包含一些 1。我想以对角线模式向后扩展 100 和向前扩展 100 :)
  • 向量除了中间是否总是有零?

标签: matlab matrix vector diagonal


【解决方案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 表示您希望向左和向右“重复”向量 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

【讨论】:

  • 我的直觉认为,为了提高性能,我们可以使用零初始化数组,然后以矢量化方式将值设置到其中,例如 bsxfun(@plus 以获得那些线性索引,这些重复值是放。如果 OP 关心性能,我想这一切都是值得的。
  • @Divakar 这很有趣。我将不得不对此进行试验,但这当然是假设x 数组在向量中有一些零值。如果x 也已满员,我的做法会奏效。
  • 啊等一下!我假设我们总是有一个包含很多零和一些非零的输入向量!所以,如果不是这样,改装可能是个好方法!
  • @Divakar 我必须进行实验,但这当然是个好主意。您只能访问矩阵中列总数的非零倍数的元素,而我的方法是numel(x) x (2*n + 1)。减少的访问次数应该会提供更好的性能...当然假设 x 的大小很小。
  • 非常感谢。我真的很感谢您详细介绍了!
【解决方案2】:

如果向量总是由中间的非零部分组成,您可以使用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

【讨论】:

  • OP 期望输出大小为 6x5。删除最后一列和最后一行即可!
  • @Sardar_Usama 谢谢,我想我已经纠正了。虽然我看不到输出大小与输入的关系
  • 做得很好。我很想知道convmtx 如何使用我的mod/bsxfun 方法进行基准测试。
  • @rayryeng 非常好!
  • 现在没有convmtx @rayryeng​​span>
猜你喜欢
  • 1970-01-01
  • 2019-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-02
  • 1970-01-01
  • 1970-01-01
  • 2014-07-27
相关资源
最近更新 更多