Jac*_*cob 25 matlab matrix vectorization matrix-multiplication
假设我有一个AxBxC矩阵X 和一个BxD矩阵Y.
是否有一种非循环方法,通过它我可以将每个C AxB矩阵与Y?
Zai*_*aid 17
作为个人喜好,我喜欢我的代码尽可能简洁易读.
这就是我要做的,虽然它不符合你的'无循环'要求:
for m = 1:C
Z(:,:,m) = X(:,:,m)*Y;
end
Run Code Online (Sandbox Code Playgroud)
这导致一个X d XÇ矩阵ž.
当然,您可以随时预先分配Z以通过使用来加快速度Z = zeros(A,D,C);.
gno*_*ice 15
您可以使用NUM2CELL函数在一行中将矩阵X分解为单元格数组,并使用CELLFUN在单元格中操作:
Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
Run Code Online (Sandbox Code Playgroud)
结果Z是1-by-C单元阵列,其中每个单元包含A-by-D矩阵.如果您想Z成为A-by-D-by-C矩阵,可以使用CAT函数:
Z = cat(3,Z{:});
Run Code Online (Sandbox Code Playgroud)
注意:我的旧解决方案使用MAT2CELL而不是NUM2CELL,这并不简洁:
[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false);
Run Code Online (Sandbox Code Playgroud)
这是一个单行解决方案(如果你想分成第三维,则为两个):
A = 2;
B = 3;
C = 4;
D = 5;
X = rand(A,B,C);
Y = rand(B,D);
%# calculate result in one big matrix
Z = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])' * Y;
%'# split into third dimension
Z = permute(reshape(Z',[D A C]),[2 1 3]);
Run Code Online (Sandbox Code Playgroud)
因此现在:Z(:,:,i)包含结果X(:,:,i) * Y
说明:
以上可能看起来令人困惑,但这个想法很简单.首先,我从第三个维度开始,X沿着第一个维度进行垂直连接:
XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))
Run Code Online (Sandbox Code Playgroud)
...困难在于它C是一个变量,因此你无法使用cat或vertcat来推广该表达式.接下来我们乘以Y:
ZZ = XX * Y;
Run Code Online (Sandbox Code Playgroud)
最后,我将其分解为第三维:
Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);
Run Code Online (Sandbox Code Playgroud)
所以你可以看到它只需要一个矩阵乘法,但你必须在前后重新整形矩阵.
我正在接近完全相同的问题,着眼于最有效的方法.我看到大约有三种方法,没有使用外部库(即mtimesx):
我最近比较了所有三种方法,看看哪种方法最快.我的直觉是(2)将成为胜利者.这是代码:
% generate data
A = 20;
B = 30;
C = 40;
D = 50;
X = rand(A,B,C);
Y = rand(B,D);
% ------ Approach 1: Loop (via @Zaid)
tic
Z1 = zeros(A,D,C);
for m = 1:C
Z1(:,:,m) = X(:,:,m)*Y;
end
toc
% ------ Approach 2: Reshape+Permute (via @Amro)
tic
Z2 = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])' * Y;
Z2 = permute(reshape(Z2',[D A C]),[2 1 3]);
toc
% ------ Approach 3: cellfun (via @gnovice)
tic
Z3 = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
Z3 = cat(3,Z3{:});
toc
Run Code Online (Sandbox Code Playgroud)
所有这三种方法产生了相同的输出(p!),但令人惊讶的是,循环是最快的:
Elapsed time is 0.000418 seconds.
Elapsed time is 0.000887 seconds.
Elapsed time is 0.001841 seconds.
Run Code Online (Sandbox Code Playgroud)
请注意,从一个试验到另一个试验,时间可能会有很大变化,有时(2)出现的速度最慢.数据量越大,这些差异就越大.但与很多更大的数据,(3)次(2).循环方法仍然是最好的.
% pretty big data...
A = 200;
B = 300;
C = 400;
D = 500;
Elapsed time is 0.373831 seconds.
Elapsed time is 0.638041 seconds.
Elapsed time is 0.724581 seconds.
% even bigger....
A = 200;
B = 200;
C = 400;
D = 5000;
Elapsed time is 4.314076 seconds.
Elapsed time is 11.553289 seconds.
Elapsed time is 5.233725 seconds.
Run Code Online (Sandbox Code Playgroud)
但是如果循环维度比其他维度大得多,则循环方法可能比(2)慢.
A = 2;
B = 3;
C = 400000;
D = 5;
Elapsed time is 0.780933 seconds.
Elapsed time is 0.073189 seconds.
Elapsed time is 2.590697 seconds.
Run Code Online (Sandbox Code Playgroud)
所以(2)在这个(可能是极端的)情况下以一个重要因素获胜.可能没有一种方法在所有情况下都是最佳的,但循环仍然相当不错,在许多情况下最好.在可读性方面也是最好的.环走!