新解决方案:
这是基于旧解决方案的新解决方案,它解决了先前给出的公式。问题中的代码实际上是对该公式的修改,其中第三维中两个矩阵之间的重叠被重复移动(类似于沿数据第三维的卷积)。我之前给出的解决方案只计算了问题中代码的 last 迭代的结果(即k = kend)。所以,这里有一个完整的解决方案,它应该比问题中kend 大约 1000 的代码更高效:
kend = size(A,3); %# Get the value for kend
C = zeros(3,3,kend); %# Preallocate the output
Anew = reshape(flipdim(A,3),3,[]); %# Reshape A into a 3-by-3*kend matrix
Bnew = reshape(permute(B,[1 3 2]),[],3); %# Reshape B into a 3*kend-by-3 matrix
for k = 1:kend
C(:,:,k) = Anew(:,3*(kend-k)+1:end)*Bnew(1:3*k,:); %# Index Anew and Bnew so
end %# they overlap in steps
%# of three
即使只使用kend = 100,这个解决方案对我来说比问题中的解决方案快大约 30 倍,比纯基于 for 循环的解决方案快大约 4 倍(这将涉及 5 个循环!)。请注意,下面关于浮点精度的讨论仍然适用,因此这是正常的,并且预计您会看到在 relative floating-point accuracy 顺序上的解决方案之间存在细微差异。
旧解决方案:
根据您在评论中链接到的公式:
看来您实际上想要做的事情与您在问题中提供的代码不同。假设 A 和 B 是 3×3×k 矩阵,则结果 C 应该是一个 3×3 矩阵,并且链接中的公式写成一组嵌套的 for 循环将看起来像这样:
%# Solution #1: for loops
k = size(A,3);
C = zeros(3);
for i = 1:3
for j = 1:3
for r = 1:3
for l = 0:k-1
C(i,j) = C(i,j) + A(i,r,k-l)*B(r,j,l+1);
end
end
end
end
现在,可以通过适当地重塑和重组 A 和 B 来执行此操作而无需任何 for 循环:
%# Solution #2: matrix multiply
Anew = reshape(flipdim(A,3),3,[]); %# Create a 3-by-3*k matrix
Bnew = reshape(permute(B,[1 3 2]),[],3); %# Create a 3*k-by-3 matrix
C = Anew*Bnew; %# Perform a single matrix multiply
您甚至可以修改问题中的代码,以创建一个具有单个循环的解决方案,该循环执行您的 3×3 子矩阵的矩阵乘法:
%# Solution #3: mixed (loop and matrix multiplication)
k = size(A,3);
C = zeros(3);
for l = 0:k-1
C = C + A(:,:,k-l)*B(:,:,l+1);
end
那么现在的问题是:这些方法中哪一种更快/更清洁?
嗯,“更干净”是非常主观的,老实说,我无法告诉您以上哪些代码更容易理解操作正在做什么。第一个解决方案中的所有循环和变量使得跟踪正在发生的事情有点困难,但它清楚地反映了公式。第二种解决方案将其全部分解为一个简单的矩阵运算,但很难看出它与原始公式有何关系。第三种解决方案似乎介于两者之间。
所以,让我们把速度作为决胜局。如果我为k 的多个值对上述解决方案计时,我会得到这些结果(以秒为单位执行给定解决方案的 10,000 次迭代,MATLAB R2010b):
k | loop | matrix multiply | mixed
-----+--------+-----------------+--------
5 | 0.0915 | 0.3242 | 0.1657
10 | 0.1094 | 0.3093 | 0.2981
20 | 0.1674 | 0.3301 | 0.5838
50 | 0.3181 | 0.3737 | 1.3585
100 | 0.5800 | 0.4131 | 2.7311 * The matrix multiply is now fastest
200 | 1.2859 | 0.5538 | 5.9280
好吧,事实证明,对于较小的 k 值(大约 50 或更小),for 循环解决方案实际上胜出,再次表明 for 循环并不像过去认为的那样“邪恶”旧版本的 MATLAB。在某些情况下,它们可能比聪明的矢量化更有效。然而,当k 的值大于 100 左右时,向量化矩阵乘法解决方案开始胜出,随着k 的增加,其缩放比 for 循环解决方案要好得多。由于我不确定的原因,混合的 for-loop/matrix-multiply 解决方案严重扩展。
所以,如果您希望 k 很大,我会选择矢量化矩阵乘法解决方案。要记住的一件事是,您从每个解决方案(矩阵C)中获得的结果将略有不同(在floating-point precision 的级别上),因为每个解决方案执行的加法和乘法的顺序是不同的,从而导致rounding errors的累积差异。简而言之,这些解决方案的结果之间的差异应该可以忽略不计,但您应该意识到这一点。