我有一个8x8的矩阵,例如A=rand(8,8)
。我需要做的是沿对角线将所有2x2矩阵子集化。这意味着我需要保存矩阵A(1:2,1:2)
,A(3:4,3:4)
,A(5:6,5:6)
,A(7:8,7:8)
。为了更好地说明自己,我使用的当前版本如下:
AA = rand(8,8);
BB = zeros(8,2);
for i = 1:4
BB(2*i-1:2*i,:) = AA(2*i-1:2*i,2*i-1:2*i);
end
这对于较小的AA
矩阵和较小的AA
子矩阵都适用,但是随着大小的显着增长(甚至可以达到50,000x50,000),使用for
这样的循环以上那一项不可行。有没有办法实现上述目的而没有循环?我考虑过其他可能利用上下三角形矩阵的方法,但是即使在某些时候,这些方法似乎也需要循环。任何帮助表示赞赏!
答案 0 :(得分:2)
答案 1 :(得分:2)
这里是一种替代方案,它不生成完整的矩阵来选择块对角线,如Luis Mendo' answer中那样,而是直接生成这些元素的索引。对于非常大的矩阵,这可能会更快,因为在这种情况下创建索引矩阵会很昂贵。
AA = rand(8,8); % example matrix. Assumed square
n = 2; % submatrix size. Assumed to divide the size of A
m=size(AA,1);
bi = (1:n)+(0:m:n*m-1).'; % indices for elements of one block
bi = bi(:); % turn into column vector
di = 1:n*(m+1):m*m; % indices for first element of each block
BB = AA(di+bi-1); % extract the relevant elements
BB = reshape(BB,n,[]).' % put these elements in the desired order
AA = rand(5000); % couldn't do 50000x50000 because that's too large!
n = 2;
BB1 = method1(AA,n);
BB2 = method2(AA,n);
BB3 = method3(AA,n);
assert(isequal(BB1,BB2))
assert(isequal(BB1,BB3))
timeit(@()method1(AA,n))
timeit(@()method2(AA,n))
timeit(@()method3(AA,n))
% OP's loop
function BB = method1(AA,n)
m = size(AA,1);
BB = zeros(m,n);
for i = 1:m/n
BB(n*(i-1)+1:n*i,:) = AA(n*(i-1)+1:n*i,n*(i-1)+1:n*i);
end
end
% Luis' mask matrix
function BB = method2(AA,n)
mask = repelem(logical(eye(size(AA,1)/n)), n, n);
BB = reshape(permute(reshape(AA(mask), n, n, []), [1 3 2]), [], n);
end
% Cris' indices
function BB = method3(AA,n)
m = size(AA,1);
bi = (1:n)+(0:m:n*m-1).';
bi = bi(:);
di = 0:n*(m+1):m*m-1;
BB = reshape(AA(di+bi),n,[]).';
end
在我的计算机上,使用MATLAB R2017a,我得到:
method1
(OP循环):0.0034 s method2
(路易斯掩模矩阵):0.0599 s method3
(克里斯指数):1.5617e-04 s 请注意,对于5000x5000的数组,此答案中的方法比循环快约20倍,而循环比Luis解决方案快约20倍。
对于较小的矩阵,情况略有不同,Luis的方法几乎快于50x50矩阵的循环代码的两倍(尽管此方法仍然比其快3倍左右)。