我有两个采用以下形式的数组:
0…0…0 0 0 0…0 0…0…0 0 0 0…0
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
0…0 0 0 0 0…0 0…0 0 0 0 0…0
A = 0…0 1 2 3 0…0 B = 0…0 9 8 7 0…0
0…0 4 5 6 0…0 0…0 6 5 4 0…0
0…0 0 0 0 0…0 0…0 0 0 0 0…0
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
0…0…0 0 0 0…0 0…0…0 0 0 0…0
A
和B
的非零区域的大小可能不完全相同,但上图已经变得有点笨拙。
最终,我所追求的价值是sum(sum(A .* B))
。我觉得必须有一种方法只能将非零元素相乘,但我能想出的每一种方法似乎都会导致MATLAB复制矩阵,这完全破坏了通过减少操作数量而获得的任何收益。 B
被重用于内循环的几次迭代,因此我可以通过许多循环迭代在B
上分摊昂贵的计算。
到目前为止,我尝试了以下方法:
天真的方法:
function C = innerLoop(A, B)
C = sum(sum(A .* B))
end
使用此功能, innerLoop
在86,000次通话中大约需要4.3秒。 (基于MATLAB的“运行和时间”功能。)
缩小B
首先:
function B = resize(self, B1)
rows = abs(sum(B, 2)) > 1e-4;
top = find(rows, 1, 'first');
bot = find(rows, 1, 'last');
cols = abs(sum(B, 1)) > 1e-4;
left = find(cols, 1, 'first');
right = find(cols, 1, 'last');
self.Rows = top:bot; % Store in class properties for use in inner loop
self.Cols = left:right; % Store in class properties for use in inner loop
B = B(top:bot, left:right);
end
function C = innerLoop(A, B)
result = A(self.Rows, self.Cols) .* B;
C = sum(sum(result));
end
我对这种方法的希望是MATLAB会意识到我没有写A
并且没有写副本,但这种方法在innerLoop
中花了大约6.8秒。
我还尝试仅计算innerLoop
之外的偏移量,希望MATLAB能够理解我在两个矩阵上使用相同的下标来优化事物的事实:
function B = resize(self, B1)
rows = abs(sum(B, 2)) > 1e-4;
top = find(rows, 1, 'first');
bot = find(rows, 1, 'last');
cols = abs(sum(B, 1)) > 1e-4;
left = find(cols, 1, 'first');
right = find(cols, 1, 'last');
self.Rows = top:bot; % Store in class properties for use in inner loop
self.Cols = left:right; % Store in class properties for use in inner loop
end
function C = innerLoop(A, B)
result = A(self.Rows, self.Cols) .* B(self.Rows, self.Cols);
C = sum(sum(result));
end
不幸的是,这是最慢的约8.6秒。
我也尝试使用以下代码进行循环:
function C = innerLoop(A, B)
C = 0;
for i = self.Rows
for j = self.Cols
C = C + field(i, j) * self.Sensitivity.Z(i, j);
end
end
end
我知道在MATLAB中循环过去非常慢,但我读过一些文章表明它比以前快得多。也就是说,如果循环版本运行完毕,我会告诉你需要多长时间,但现在已经过了几分钟了。
有关如何优化此功能的任何建议都将非常感谢!
答案 0 :(得分:6)
您可以使用稀疏矩阵来解决此问题。 Matlab自动处理不同大小的“非稀疏部分”。要获得稀疏矩阵,使用sparse
- 函数。之后,您可以进行逐元素乘法,然后将C
中的所有元素加到一个单独的行中。
A = [0 0 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 1 2 3 0 0;
0 0 4 5 6 0 0;
0 0 0 0 0 0 0;
0 0 0 0 0 0 0];
B = [0 0 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 9 8 7 0 0;
0 0 6 5 4 0 0;
0 0 0 0 0 0 0;
0 0 0 0 0 0 0];
A = sparse(A);
B = sparse(B);
C = A .* B;
sum(C(:))
答案 1 :(得分:3)
这是我的初始帖子的重写
我的立场得到了纠正。我不知道我以前的测试出了什么问题。我认为它可能是稀疏算法的32比64位实现,但不是偶数。在两台不同的机器上仔细重新运行基准测试后,sparse
方法将获胜。
基准代码:
function ExecTimes = bench_sum_sparse
nOrder = (1:9).' * 10.^(2:3) ; nOrder = [nOrder(:) ; (1:2).'*1e4] ;
%// nOrder = (1:30)*100 ;
npt = numel(nOrder) ;
ExecTimes = zeros( npt , 3 ) ;
fprintf('\n%s%5d \n','Calculating for N = ',0) ;
for k = 1:npt
% // sample data
N = nOrder(k) ;
fprintf('\b\b\b\b\b\b%5d\n',N) ; % // display progress
A = zeros(N) ;
B = A ;
innerMat = (1:10).'*(1:10) ; %'
ixInnerMat = innerMat + N/2 - 5 ;
A(ixInnerMat) = innerMat ;
B(ixInnerMat) = innerMat ;
% // benchmark
f1 = @() innerLoop(A,B) ;
ExecTimes(k,1) = timeit( f1 ) ;
clear f1
f2 = @() sum_logicIndex(A, B) ;
ExecTimes(k,2) = timeit( f2 ) ;
clear f2
A = sparse(A);
B = sparse(B);
f3 = @() sum_sparse(A,B) ;
ExecTimes(k,3) = timeit( f3 ) ;
clear f3
%// checksum1 = f1() - f2 ()
%// checksum2 = f1() - f3 ()
end
end
function C = innerLoop(A, B)
C = sum(sum(A .* B)) ;
end
function C = sum_logicIndex(A,B)
idx = A>0 & B>0 ;
C = sum(sum(A(idx).*B(idx))) ;
end
function C = sum_sparse(A,B)
C = A .* B;
C = sum(C(:)) ;
end
所有测试都在Matlab 2013b上运行
64位机器:Intel I7-3820 @ 3.6GHz - 16 GB RAM - Windows 7
32位机器:Intel E2200 @ 2.2GHz - 3GB RAM - Windows 8.1