将3D矩阵与2D矩阵相乘

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);.

  • +1因为它也比gnovice和amro的精细解决方案更快. (6认同)
  • -1:因为无论您的免责声明如何,这都不是真正的解决方案.如果您对简洁性或可读性有任何意见,请留下评论. (4认同)

gno*_*ice 15

您可以使用NUM2CELL函数在一行中将矩阵X分解为单元格数组,并使用CELLFUN在单元格中操作:

Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
Run Code Online (Sandbox Code Playgroud)

结果Z1-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)

  • 使用此解决方案,循环在cellfun内.但它比amro提供的解决方案快10%(在大型矩阵上,在MATLAB内存耗尽之前不久). (2认同)
  • 男人,谁会想到这样一个简单的问题会引起争议? (2认同)

Amr*_*mro 8

这是一个单行解决方案(如果你想分成第三维,则为两个):

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是一个变量,因此你无法使用catvertcat来推广该表达式.接下来我们乘以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)

所以你可以看到它只需要一个矩阵乘法,但你必须在前后重新整形矩阵.


Nol*_*way 5

我正在接近完全相同的问题,着眼于最有效的方法.我看到大约有三种方法,没有使用外部库(即mtimesx):

  1. 循环通过3D矩阵的切片
  2. repmat-and-permute魔法
  3. cellfun乘法

我最近比较了所有三种方法,看看哪种方法最快.我的直觉是(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)在这个(可能是极端的)情况下以一个重要因素获胜.可能没有一种方法在所有情况下都是最佳的,但循环仍然相当不错,在许多情况下最好.在可读性方面也是最好的.环走!