如何计算两个矩阵的外积平方和减去Matlab中的公共矩阵?

时间:2018-11-12 11:30:57

标签: matlab matrix-multiplication

假设存在三个n * n个矩阵XYS。如何快速计算以下标量 b

for i = 1:n
  b = b  + sum(sum((X(i,:)' * Y(i,:) - S).^2));
end

计算成本为O(n ^ 3)。存在fast way to compute the outer product of two matrices。具体来说,矩阵 C

for i = 1:n
  C = C + X(i,:)' * Y(i,:);
end
可以在没有for循环C = A.'*B的情况下计算

,后者仅为O(n ^ 2)。是否存在一种更快的方法来计算b

2 个答案:

答案 0 :(得分:4)

您可以使用:

X2 = X.^2;
Y2 = Y.^2;
S2 = S.^2;
b = sum(sum(X2.' * Y2 - 2 * (X.' * Y ) .* S + n * S2));

举个例子

b=0;
for i = 1:n
   b = b  + sum(sum((X(i,:).' * Y(i,:) - S).^2));
end

我们首先可以将求和带出循环:

b=0;
for i = 1:n
  b = b  + (X(i,:).' * Y(i,:) - S).^2;
end
b=sum(b(:))

知道我们可以将(a - b)^2写为a^2 - 2*a*b + b^2

b=0;
for i = 1:n
  b = b  + (X(i,:).' * Y(i,:)).^2 - 2.* (X(i,:).' * Y(i,:)) .*S + S.^2;
end
b=sum(b(:))

我们知道(a * b) ^ 2a^2 * b^2相同:

X2 = X.^2;
Y2 = Y.^2;
S2 = S.^2;
b=0;
for i = 1:n
  b = b  + (X2(i,:).' * Y2(i,:)) - 2.* (X(i,:).' * Y(i,:)) .*S + S2;
end
b=sum(b(:))

现在我们可以分别计算每个项:

 b = sum(sum(X2.' * Y2 - 2 * (X.' * Y ) .* S + n * S2));

这是Octave中测试的结果,该测试将我的方法与@AndrasDeak提供的其他两种方法以及大小为500*500的输入的基于原始循环的解决方案进行了比较:

===rahnema1 (B)===
Elapsed time is 0.0984299 seconds.

===Andras Deak (B2)===
Elapsed time is 7.86407 seconds.

===Andras Deak (B3)===
Elapsed time is 2.99158 seconds.

===Loop solution===
Elapsed time is 2.20357 seconds


n=500;
X= rand(n);
Y= rand(n);
S= rand(n);

disp('===rahnema1 (B)===')
tic
    X2 = X.^2;
    Y2 = Y.^2;
    S2 = S.^2;
    b=sum(sum(X2.' * Y2 - 2 * (X.' * Y ) .* S + n * S2));
toc
disp('===Andras Deak (B2)===')
tic
    b2 = sum(reshape((permute(reshape(X, [n, 1, n]).*Y, [3,2,1]) - S).^2, 1, []));
toc
disp('===Andras Deak (B3)===')
tic
    b3 = sum(reshape((reshape(X, [n, 1, n]).*Y - reshape(S.', [1, n, n])).^2, 1, []));
toc
tic
    b=0;
    for i = 1:n
      b = b  + sum(sum((X(i,:)' * Y(i,:) - S).^2));
    end
toc

答案 1 :(得分:3)

您可能无法节省时间的复杂性,但是您可以利用向量化来摆脱循环,并尽可能利用低级代码和缓存。它实际上是否更快取决于您的尺寸,因此您需要进行一些计时测试以查看是否值得:

% dummy data
n = 3;
X = rand(n);
Y = rand(n);
S = rand(n);

% vectorize
b2 = sum(reshape((permute(reshape(X, [n, 1, n]).*Y, [3,2,1]) - S).^2, 1, []));

% check
b - b2 % close to machine epsilon i.e. zero

发生的事情是,我们在其中一个数组中插入了一个新的单例维度,最后得到了一个大小为[n, 1, n]的数组,而其中一个数组为[n, n],后者与{{1 }}。重叠的第一个索引对应于循环中的[n, n, 1],其余两个索引对应于每个i的二元乘积的矩阵索引。然后,我们对索引进行置换,以将“ i”索引排在最后,以便我们可以再次使用(隐式)i大小的S广播结果。然后,我们得到的是大小为[n, n, 1]的矩阵,其中前两个索引是原始文档中的矩阵索引,最后一个对应于[n, n, n]。然后,我们只需要取平方并求和每个项(而不是求和两次,而是将数组整形为一行并求和一次)。

上述转置i的微小变化代替了可能更快的3d数组(同样,应该计时):

S

在性能方面,b3 = sum(reshape((reshape(X, [n, 1, n]).*Y - reshape(S.', [1, n, n])).^2, 1, [])); 是免费的(它仅重新解释数据,不会复制),但是reshape /转置通常会在复制数据时导致性能下降。