如何改进MATLAB中用于从其他矩阵替换矩阵元素的代码?

3

所以我有这4个相同大小的矩阵:

k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];

我希望您能将每个矩阵的元素放入零矩阵的对角线位置,从而创建类似于以下内容的结果:
f =  0.5 2.0 0   0   0   0   0   0 
     1.7 2.5 0   0   0   0   0   0
     0   0   1.0 4.0 0   0   0   0  
     0   0   3.0 5.0 0   0   0   0  
     0   0   0   0   1.5 6.0 0   0  
     0   0   0   0   4.5 7.5 0   0  
     0   0   0   0   0   0   2.0 8.0
     0   0   0   0   0   0   6.0 10.0

这是我想到的内容:
f = zeros(8,8);
k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];

for i = 1:2:8 %odd index number
    for j = 2:2:8 %even index number
        f(i,i) = k(1,i)
        f(i,j) = l(1,i)
        f(j,i) = m(1,i)
        f(j,j) = n(i,1)
    end;
end;
disp(f)

但是结果将元素放置在矩阵 f=zero(8,8) 的对角线之外,我总是会得到 Index exceeds matrix dimensions. 的错误。

我能否获得一些关于如何修复此代码的指针?


虽然与您的问题无关,但我仍然想指出这一点。klmn是向量(1D数组)。您只需要指定一个索引来访问该值,即k(i)而不是k(1,i) - Anthony
除了@anthony所说的之外:k(1,i)越界,因为它是一个列向量,第一个索引是行号。请尝试使用k(i,1)。但是,作为学习索引的练习,k(i)更简单、更惯用。 - Cris Luengo
5个回答

4
我认为你想要写的是这个:

我认为您打算写这个:

for i = 2:2:8
    f(i-1,i-1) = k(i/2)
    f(i-1,i) = l(i/2)
    f(i,i-1) = m(i/2)
    f(i,i) = n(i/2)
end

虽然有更简单的方法可以实现同样的功能。例如,看看函数blkdiag

1
k((i+1)/2):k((i+1)/2)for(int i=0; i<n; i++):for(int i=0; i<n; i++)if(x>y):如果(x>y)return ans;:返回ans; - Anthony
我现在明白了,非常感谢您的帮助!我还会检查blkdiag函数,以使这段代码更简单! - ynr
@Anthony:完全正确!我根本没有注意到写那个,是吧? :) - Cris Luengo

4
如果您需要紧凑的解决方案,那么这里有一个有趣的解决方案,使用 eyerepelem逻辑索引
>> f = zeros(8);
>> f(repelem(eye(4, 'logical'), 2, 2)) = [k m l n].';

f =

    0.5000    2.0000         0         0         0         0         0         0
    1.7000    2.5000         0         0         0         0         0         0
         0         0    1.0000    4.0000         0         0         0         0
         0         0    3.0000    5.0000         0         0         0         0
         0         0         0         0    1.5000    6.0000         0         0
         0         0         0         0    4.5000    7.5000         0         0
         0         0         0         0         0         0    2.0000    8.0000
         0         0         0         0         0         0    6.0000   10.0000

这个不错!我知道人们会给出非常简短的答案。 :) 不知何故,我希望 kron 是解决方案的一部分,但我看不出来... - Cris Luengo
@CrisLuengo:请检查我的编辑。我一开始使用了kron,但后来改用repelem,因为我认为它更有效率。 - gnovice
啊,那里有一个“kron”!太棒了!我得学会在某个时候使用它... :p - Cris Luengo
这真的很好而且高效!感谢您提供的建议! - ynr

2

Cris Luengo的回答比我这里更灵活,读起来更好,但如果您将会使用MATLAB进行大量工作,那么这是您可能想要学习的内容。

(这种方法通常执行得更好,因为它消除了使用for循环的需要。)

k = [0.5;1.0;1.5;2.0];
l = [2;4;6;8];
m = [1.7;3.0;4.5;6.0];
n = [2.5;5.0;7.5;10.0];

f = zeros(8); % to create a square matrix, you only need to specify the size with one value

f(1:18:end) = k;
f(9:18:end) = l;
f(2:18:end) = m;
f(10:18:end) = n;

在这里,即使f是一个8*8的矩阵,它也被用作向量(数组)。在Matlab中,您可以通过指定行和列号或线性索引来访问矩阵中的单个元素。
更多细节可以在此处找到:https://uk.mathworks.com/help/matlab/math/matrix-indexing.html(请参见线性索引)。

@Wolfie 确实。我之前想要更详细地展示这个想法,因此使用了 idx_k = 1:18:nf; 等等……后来我决定简化代码,并没有意识到 nf 也变得多余了。回答已经修正。 - Anthony

2

另一种选择是先构建对角线和两个非对角向量,然后使用 diag 创建矩阵,最后将它们相加。

%Data
k = [0.5;1.0;1.5;2.0];
l = [2;4;6;8];
m = [1.7;3.0;4.5;6.0];
n = [2.5;5.0;7.5;10.0];

%Construct vectors
diagonal = [1;0].*k.' + [0;1].*n.'; %Diagonal of the matrix
offu = [1;0].*l'; %Upper diagonal
offl = [1;0].*m'; %Lower diagonal

A = diag(diagonal(:)) + diag(offu(1:end-1),1) + diag(offl(1:end-1),-1);    

我不知道它是否比其他答案更快,但我喜欢它透明地显示正在发生的事情。

注意:为了构造变量diagonaloffuoffl,我使用了隐式扩展,需要Matlab R2016b或更新版本。如果您使用的是旧版本,则可以使用bsxfun代替。


1
抱歉,我错了,一切都被移位了。我的先前答案完全不正确...
我仍然会选择使用blkdiag的某些东西,你只需要重新分配矩阵:
k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];

Y = arrayfun(@(i) [k(i), l(i); m(i), n(i)], 1:4, 'UniformOutput', false);
blkdiag(Y{:})

X = cat(3, [k, m], [l, n]);
blkdiag(squeeze(X(1,:,:)), squeeze(X(2,:,:)), squeeze(X(3,:,:)), squeeze(X(4,:,:)))

2
这样做并不能得到所需的结果,如问题所示...每个块不是单独的向量,而是块内每个对应的元素来自同一个向量。 - Wolfie
抱歉 @Wolfie,确实如此。已修复。 - Christian Heigele

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