假设我有一个 AxBxC 矩阵
X
和一个 BxD 矩阵 Y
。是否有一种非循环方法,可以将 Y
与每个 C AxB 矩阵相乘?X
和一个 BxD 矩阵 Y
。是否有一种非循环方法,可以将 Y
与每个 C AxB 矩阵相乘?个人偏好,我喜欢我的代码尽可能简洁易读。
以下是我会做的做法,但不符合你的“无循环”要求:
for m = 1:C
Z(:,:,m) = X(:,:,m)*Y;
end
这将导致一个 A x D x C 的矩阵 Z。
当然,你可以通过使用 Z = zeros(A,D,C);
来预先分配Z以加快速度。
Z = zeros([A D C]);
预分配Z! (为可读性加1) - FlorisX
分解成单元数组,然后再使用CELLFUN函数在单元格之间操作,一行代码即可完成。Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);
结果Z
是一个1×C的单元数组,其中每个单元包含一个A×D的矩阵。如果你想让Z
成为一个A×D×C的矩阵,你可以使用CAT函数:
Z = cat(3,Z{:});
注意:我的旧解决方法使用了 MAT2CELL 而不是 NUM2CELL,这种方法不够简洁。
[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false);
bsxfun
的方法来完成这个任务,而你的cellfun
实现正好符合要求。我原以为“非循环”条件足以清楚地表达我的意图,看来并不是! - Jacob这里有一个一行解决方案(如果您想分成第三个维度,则为两行):
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]);
Z(:,:,i)
包含了 X(:,:,i) * Y
的结果。
X
的第三维为起点,在第一个维度上进行垂直连接:XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))
...难点在于C
是一个变量,因此您无法使用cat或vertcat来概括该表达式。接下来,我们将其乘以Y
:
ZZ = XX * Y;
Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);
所以您可以看到,它只需要进行一次矩阵乘法,但是您需要在之前和之后对矩阵进行重塑。
bsxfun
的解决方案,但这看起来很有趣。 - Jacob% 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
这三种方法输出的结果都一样(太好了!),但令人惊讶的是,循环方式最快:
Elapsed time is 0.000418 seconds.
Elapsed time is 0.000887 seconds.
Elapsed time is 0.001841 seconds.
% 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.
但是循环方法 可能会 比(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.
因此,在这种(可能极端)情况下,(2)获胜的优势很大。也许没有一种方法是在所有情况下都最优的,但是循环仍然非常好,并且在许多情况下是最好的。从可读性角度来看,它也是最佳的。继续使用循环!
C=mmx('mul',X,Y);
这里有一个所有可能方法的基准。更详细的内容请参考此问题。
1.6571 # FOR-loop
4.3110 # ARRAYFUN
3.3731 # NUM2CELL/FOR-loop/CELL2MAT
2.9820 # NUM2CELL/CELLFUN/CELL2MAT
0.0244 # Loop Unrolling
0.0221 # MMX toolbox <===================
nT = 100;
t = 2*pi*linspace (0,1,nT)’;
# 2 experiments measuring 3 signals at nT timestamps
signals = zeros(nT,3,2);
signals(:,:,1) = [sin(2*t) cos(2*t) sin(4*t).^2];
signals(:,:,2) = [sin(2*t+pi/4) cos(2*t+pi/4) sin(4*t+pi/6).^2];
sT(:,:,1) = signals(:,:,1)’;
sT(:,:,2) = signals(:,:,2)’;
G = ndmult (signals,sT,[1 2]);
原始来源。我添加了内联注释。
function M = ndmult (A,B,dim)
dA = dim(1);
dB = dim(2);
# reshape A into 2d
sA = size (A);
nA = length (sA);
perA = [1:(dA-1) (dA+1):(nA-1) nA dA](1:nA);
Ap = permute (A, perA);
Ap = reshape (Ap, prod (sA(perA(1:end-1))), sA(perA(end)));
# reshape B into 2d
sB = size (B);
nB = length (sB);
perB = [dB 1:(dB-1) (dB+1):(nB-1) nB](1:nB);
Bp = permute (B, perB);
Bp = reshape (Bp, sB(perB(1)), prod (sB(perB(2:end))));
# multiply
M = Ap * Bp;
# reshape back to original format
s = [sA(perA(1:end-1)) sB(perB(2:end))];
M = squeeze (reshape (M, s));
endfunction
不行。有几种方法,但它总是以循环、直接或间接的方式出现。
只是为了满足我的好奇心,你为什么要那样做呢?
function [C] = tensor(A,B)
C = squeeze( reshape( repmat(A(:), 1, numel(B)).*B(:).' , [size(A),size(B)] ) );
end
2) 收缩运算: 这里的A和B是要沿着i和j维度进行收缩运算的张量。当然,这些维度的长度应该相等。代码中没有对此进行检查(这会使代码变得复杂),但除此之外,它运行良好。
function [C] = tensorcontraction(A,B, i,j)
sa = size(A);
La = length(sa);
ia = 1:La;
ia(i) = [];
ia = [ia i];
sb = size(B);
Lb = length(sb);
ib = 1:Lb;
ib(j) = [];
ib = [j ib];
% making the i-th dimension the last in A
A1 = permute(A, ia);
% making the j-th dimension the first in B
B1 = permute(B, ib);
% making both A and B 2D-matrices to make use of the
% matrix multiplication along the second dimension of A
% and the first dimension of B
A2 = reshape(A1, [],sa(i));
B2 = reshape(B1, sb(j),[]);
% here's the implicit implication that sa(i) == sb(j),
% otherwise - crash
C2 = A2*B2;
% back to the original shape with the exception
% of dimensions along which we've just contracted
sa(i) = [];
sb(j) = [];
C = squeeze( reshape( C2, [sa,sb] ) );
end
我会考虑使用递归,但那是你能做的唯一的非循环方法。