代码的矢量化

时间:2017-03-01 23:14:37

标签: matlab vectorization

以下是我的代码。循环部分非常慢。我想知道是否有办法对循环部分进行编辑。

N = 1000000;
A = rand(N,3);
B = rand(N,3);
Dist = sqrt(sum((A - B).^2,2));
R = 2;
id = rangesearch(A,A,0.01);
result = zeros(N,1);
for i = 1:N
    idx = id{i}';
    v1 = A(i,:) - A(idx,:);
    v2 = A(i,:) - B(idx,:);
    C = cross(v1,v2,2);
    D = sqrt(sum(C.^2,2))./Dist(idx);
    result(i) = sum(2 * sqrt(R^2 - D.^2));
end

这里,A和B是记录N个点的3D坐标的矩阵。首先,我想找到矩阵A中一个点的邻居,比如点Ai,其中一个邻居是Aj。我想计算从Ai到Aj-Bj线的距离。这就是我计算交叉乘积的原因。最后,我将所有距离加起来。现在,这段代码在我的电脑上运行500秒。那么有没有办法让我的代码运行得更快或以其他任何方式更快地实现这一目标?感谢。

1 个答案:

答案 0 :(得分:1)

你的for循环实际上非常快。

正如@EBH在评论中所提到的,您的代码应该在最新的2016版本中运行,但由于我使用的是早期的2015版本,因此不支持隐式扩展。

原始声明:rangesearch(A,A,0.01)并不保证您可以为每个点获得单个邻居。事实上,当我使用N=10时,ID始终为{1 2 3 4 5 6 7 8 9 10}

固定for循环方法:

tic
result = zeros(N,1);
for i = 1:N
    idx = id{i}';
    v1 = bsxfun(@minus, A(i,:), A(idx,:));
    v2 = bsxfun(@minus, A(i,:), B(idx,:));
    C = cross(v1,v2,2);
    D = sqrt(sum(C.^2,2))./Dist(idx);
    result(i) = sum(2 * sqrt(R^2 - D.^2));
end
toc

Elapsed time is 0.077025 seconds.

方法2:枚举v1和v2之间的所有可能组合

tic
idlen = cellfun(@(x) length(x),id);
idai = cell2mat(arrayfun(@(ii) repmat(ii,1,idlen(ii)), (1:N), 'UniformOutput', false));
idx2 = cell2mat(id');
V1 = A(idai,:) - A(idx2,:);
V2 = A(idai,:) - B(idx2,:);
C2 = cross(V1,V2,2);
d = @(c,id) sqrt(sum(c.^2,2))./Dist(id);
r = @(d) sum(2 * sqrt(R^2 - d.^2));
result3 = splitapply(@(c,id) r(d(c,id)), C2,idx2', idai');
toc
isequal(result,result3)


Elapsed time is 0.471092 seconds.

ans =

     1

最慢的行是splitapply

方法3:使用不保证向量化的cellfun

tic
V1 = cellfun(@(Ai,idx) bsxfun(@minus, Ai, A(idx,:)), num2cell(A,2), id, 'UniformOutput', false);
V2 = cellfun(@(Ai,idx) bsxfun(@minus, Ai, B(idx,:)), num2cell(A,2), id, 'UniformOutput', false);
C = cellfun(@(v1,v2) cross(v1,v2,2), V1,V2, 'UniformOutput', false);
D = cellfun(@(c,idx) sqrt(sum(c.^2,2))./Dist(idx), C, id, 'UniformOutput', false);
result2 = cellfun(@(d) sum(2 * sqrt(R^2 - d.^2)), D, 'UniformOutput', false);
result2 = cell2mat(result2);
toc
isequal(result,result2)

Elapsed time is 0.122700 seconds.

ans =

     1

方法4:使用parfor

tic
result = zeros(N,1);
parfor i = 1:N
    idx = id{i}';
    v1 = bsxfun(@minus, A(i,:), A(idx,:));
    v2 = bsxfun(@minus, A(i,:), B(idx,:));
    C = cross(v1,v2,2);
    D = sqrt(sum(C.^2,2))./Dist(idx);
    result(i) = sum(2 * sqrt(R^2 - D.^2));
end
toc

Elapsed time is 0.177929 seconds.

parfor抱怨ABDist无法切片,导致计算速度变慢。

编辑:上面的测试使用了N=1000。如果我使用N=10000,则方法2的时间减少40%,方法3消耗相同的时间,方法4的时间减少约90%。因此,如果您使用的是多核计算机,那么您可以选择parfor