我有两个矩阵,a1
和a2
。 a1
是3x12000,而a2
是3x4000。我想创建另一个数组3x4000,该数组是mldivide
的3x3子矩阵和{{1的3x1子矩阵的左矩阵除法(\
,a1
) }}。您可以使用for循环轻松做到这一点:
a2
但是,我想知道是否有更快的方法来做到这一点。
编辑:我知道预分配可以提高速度,我只是出于视觉目的而展示了它。
Edit2 :删除了数组的迭代增加。看来我的问题被误解了一点。我主要是想知道是否可以执行一些矩阵运算来实现我的目标,因为这可能比for循环更快,即将for ii = 1:3:12000
a = a1(:,ii:ii+2)\a2(:, ceil(ii/3));
end
调整为3x3x4000矩阵,将a1
调整为3x1x4000矩阵,然后离开矩阵一键式地划分每个级别,但是,您不能将矩阵除法与3D矩阵分开。
答案 0 :(得分:4)
您可以通过将a1
的子矩阵放在12000x12000矩阵的对角线上来创建一个包含多个独立的子方程组的方程组,如下所示:
a1(1,1) a1(1,2) a1(1,3) 0 0 0 0 0 0
a1(2,1) a1(2,2) a1(2,3) 0 0 0 0 0 0
a1(3,1) a1(3,2) a1(3,3) 0 0 0 0 0 0
0 0 0 a1(1,4) a1(1,5) a1(1,6) 0 0 0
0 0 0 a1(2,4) a1(2,5) a1(2,6) 0 0 0
0 0 0 a1(3,4) a1(3,5) a1(3,6) 0 0 0
0 0 0 0 0 0 a1(1,7) a1(1,8) a1(1,9)
0 0 0 0 0 0 a1(2,7) a1(2,8) a1(2,9)
0 0 0 0 0 0 a1(3,7) a1(3,8) a1(3,9)
,然后将其除以a2(:)
。
可以使用kron
和像这样的稀疏矩阵(source):
a1_kron = kron(speye(12000/3),ones(3));
a1_kron(logical(a1_kron)) = a1(:);
a = a1_kron\a2(:);
a = reshape(a, [3 12000/3]);
优势-速度:这比在我的PC上进行预分配的for循环快大约3-4倍。
缺点:这种方法必须考虑一个缺点:使用左除法时,Matlab会寻找求解线性方程组的最佳方法,因此,如果要求解每个子系统,独立地,将为每个子系统选择最佳方法,但是如果您将主题作为一个系统解决,则Matlab会为所有子系统一起找到最佳方法,而不是每个子系统都为最佳方法。
注意:如Stefano M的answer所示,使用一个大型方程组(使用kron
和稀疏矩阵)比使用a for循环(带有预分配)仅对于很小的方程式子系统(在我的电脑上,对于等式数量<= 7),对于较大的方程式子系统而言,使用for循环更快。
我编写并运行了一个代码,比较了解决此问题的4种不同方法:
kron
cellfun
测试:
n = 1200000;
a1 = rand(3,n);
a2 = rand(3,n/3);
disp('Method 1: for loop, no preallocation')
tic
a_method1 = [];
for ii = 1:3:n
a_method1 = [a_method1 a1(:,ii:ii+2)\a2(:, ceil(ii/3))];
end
toc
disp(' ')
disp('Method 2: for loop, with preallocation')
tic
a1_reshape = reshape(a1, 3, 3, []);
a_method2 = zeros(size(a2));
for i = 1:size(a1_reshape,3)
a_method2(:,i) = a1_reshape(:,:,i) \ a2(:,i);
end
toc
disp(' ')
disp('Method 3: kron')
tic
a1_kron = kron(speye(n/3),ones(3));
a1_kron(logical(a1_kron)) = a1(:);
a_method3 = a1_kron\a2(:);
a_method3 = reshape(a_method3, [3 n/3]);
toc
disp(' ')
disp('Method 4: cellfun')
tic
a1_cells = mat2cell(a1, size(a1, 1), repmat(3 ,1,size(a1, 2)/3));
a2_cells = mat2cell(a2, size(a2, 1), ones(1,size(a2, 2)));
a_cells = cellfun(@(x, y) x\y, a1_cells, a2_cells, 'UniformOutput', 0);
a_method4 = cell2mat(a_cells);
toc
disp(' ')
结果:
Method 1: for loop, no preallocation
Elapsed time is 747.635280 seconds.
Method 2: for loop, with preallocation
Elapsed time is 1.426560 seconds.
Method 3: kron
Elapsed time is 0.357458 seconds.
Method 4: cellfun
Elapsed time is 3.390576 seconds.
比较这四种方法的结果,您会发现使用方法3-kron
会得到稍微不同的结果:
disp(['sumabs(a_method1(:) - a_method2(:)): ' num2str(sumabs(a_method1(:)-a_method2(:)))])
disp(['sumabs(a_method1(:) - a_method3(:)): ' num2str(sumabs(a_method1(:)-a_method3(:)))])
disp(['sumabs(a_method1(:) - a_method4(:)): ' num2str(sumabs(a_method1(:)-a_method4(:)))])
结果:
sumabs(a_method1(:) - a_method2(:)): 0
sumabs(a_method1(:) - a_method3(:)): 8.9793e-05
sumabs(a_method1(:) - a_method4(:)): 0
答案 1 :(得分:1)
边际最大的改进是预分配输出矩阵,而不是增加它:
A1 = reshape(A1, 3, 3, []);
a = zeros(size(A2));
for i = 1:size(A1,3)
a(:,i) = A1(:,:,i) \ A2(:,i);
end
使用preallocate数组,如果Parallel Toolbox可用,则可以尝试parfor
此答案不再相关,因为OP改写了这个问题,以避免增长结果数组,而这是最初的主要瓶颈。
这里的问题是必须解决4000个独立的3x3线性系统。矩阵是如此之小,以至于可能会引起特别的解决方案,特别是如果该矩阵具有关于矩阵属性的一些信息(对称或不对称,条件数等)时。但是,坚持使用\
matlab运算符,加快计算速度的最佳方法是显式利用并行性,例如通过parfor
命令。
Eliahu Aaron的另一个answer的稀疏矩阵解决方案确实非常聪明,但是它的速度优势并不一般,而是取决于特定的问题大小。
使用此功能,您可以探索不同的问题大小:
function [t2, t3] = sotest(n, n2)
a1 = rand(n,n*n2);
a2 = rand(n,n2);
tic
a1_reshape = reshape(a1, n, n, []);
a_method2 = zeros(size(a2));
for i = 1:size(a1_reshape,3)
a_method2(:,i) = a1_reshape(:,:,i) \ a2(:,i);
end
t2 = toc;
tic
a1_kron = kron(speye(n2),ones(n));
a1_kron(logical(a1_kron)) = a1(:);
a_method3 = a1_kron\a2(:);
a_method3 = reshape(a_method3, [n n2]);
t3 = toc;
assert ( norm(a_method2 - a_method3, 1) / norm(a_method2, 1) < 1e-8)
实际上,n=3
的稀疏矩阵方法显然更好,但是如果增加n
,则竞争性会降低
上图是通过
获得的>> for i=1:20; [t(i,1), t(i,2)] = sotest(i, 50000); end
>> loglog(1:20, t, '*-')
我最后的评论是,使用密集\
运算符的显式循环确实非常快。稀疏矩阵公式的准确性稍差,在边缘情况下可能会出现问题;并且可以肯定的是,稀疏矩阵解不是很可读。如果要解决的系统n2
的数量非常大(> 1e6),则可能应该探索临时解决方案。
答案 2 :(得分:1)
您正在求解一系列N个系统,每个系统具有m个线性方程,N个系统的形式为
type
您可以将它们转换为Nm个线性方程的单个系统:
interface MessageDataMap {
name: string;
age: number;
gender: 'male' | 'female';
// ....
}
type MessageType = keyof MessageDataMap;
type Message = {
[K in MessageType]: {
type: K;
data: MessageDataMap[K];
};
}[MessageType];
但是,求解一个方程组比求解所有小的方程组要贵得多。通常,成本为O(n ^ 3),因此您从O(N m ^ 3)变为O((Nm)^ 3)。巨大的悲观。 (Eliahu proved me wrong here,显然可以利用矩阵的稀疏性。)
可以降低计算成本,但是您需要提供有关数据的保证。例如,如果矩阵A是正定的,则可以更廉价地求解系统。尽管如此,考虑到您要处理3x3矩阵,那里的收益将微不足道,因为这些都是非常简单的系统来解决。
如果您因为认为循环在MATLAB中本来就很慢而问这个问题,那么您应该知道,情况已不再如此,自从MATLAB在15年前获得JIT以来,情况就不再如此。如今,许多矢量化尝试都导致同样快速的代码,并且常常导致速度较慢的代码,特别是对于大数据。 (如有必要,我可以花一些时间在此处发布,以证明这一点。)
我认为一次性解决所有系统可能会减少每次调用运算符Ax = b
时MATLAB进行的检查次数。也就是说,对问题的大小和类型进行硬编码可能会始终得到改善。但是唯一的方法是编写一个MEX文件。