Matlab 代码的矢量化

Matlab 代码的矢量化,matlab,vectorization,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

下面是我的代码。循环部分相当慢。我想知道有没有一种方法可以将循环部分矢量化

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秒。那么,有没有一种方法可以让我的代码运行得更快,或者有没有其他方法可以更快地实现这个目标呢?谢谢。

您的for循环实际上相当快

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

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

固定循环方法:

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
抱怨
A
B
Dist
无法切片,从而降低了计算速度



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

这不是一个bug,它也是2016b,在测量函数运行时间方面更准确。@EBH-Ha我还在2015年。非常感谢您为我提供4种方法。我从方法2和方法3中学到了很多。实际上,我在我的项目中使用了parfor,我在问题中没有提到这一点,因为我想知道是否有一些没有循环的方法。实际上在我的项目中,N通常大于一百万,所以对我来说有点慢。关于rangesearch部分的一条评论。是当使用N=10时,很可能找不到邻居,因为所有点都是从0到1随机选取的。所以点越少意味着它们之间的距离越大。非常感谢你的帮助。