Matlab 如何矢量化查找向量中最近的点

Matlab 如何矢量化查找向量中最近的点,matlab,vectorization,euclidean-distance,bsxfun,Matlab,Vectorization,Euclidean Distance,Bsxfun,我想为大列表中的每一行找到小列表中“最近”的行,如欧几里德范数所定义(即k=3维中对应值之间的距离平方和) 我可以看到如何使用两个循环来实现这一点,但似乎应该有更好的方法使用内置矩阵运算来实现这一点。您可以使用: 正确的方法当然是使用。 但是,如果维度不太高且数据集不太大,则可以简单地使用: 除了提出的矩阵乘法方法外,还有一种无循环的矩阵乘法方法 d = bsxfun( @minus, permute( bigList, [1 3 2] ), permute( littleList, [3 1

我想为大列表中的每一行找到小列表中“最近”的行,如欧几里德范数所定义(即k=3维中对应值之间的距离平方和)

我可以看到如何使用两个循环来实现这一点,但似乎应该有更好的方法使用内置矩阵运算来实现这一点。

您可以使用:


正确的方法当然是使用。
但是,如果维度不太高且数据集不太大,则可以简单地使用:


除了提出的矩阵乘法方法外,还有一种无循环的矩阵乘法方法

d = bsxfun( @minus, permute( bigList, [1 3 2] ), permute( littleList, [3 1 2] ) ); %//diff in third dimension
d = sum( d.^2, 3 ); %// sq euclidean distance
[minDist minIdx] = min( d, [], 2 );
这种方法背后的观察结果是欧几里德距离(L2范数)

| a-b | | ^2=| | a | | ^2+| | b | ^2-2
是两个向量的点积。

方法#1 有一个内置的MATLAB函数,用于查找
“两组观测值之间的成对距离”
。使用它,您可以计算欧几里德距离矩阵,然后沿距离矩阵中的适当维度找到最小值的索引,这些索引表示
小列表中
大列表
每行的“最近”

这是它的一行-

|| a - b ||^2 = ||a||^2 + ||b||^2 - 2<a,b> 
进近#2 如果您关心性能,这里有一个利用 这里提供的大部分代码都来自

标杆管理 基准测试代码-

dim = 3;
numA = size(bigList,1);
numB = size(littleList,1);

helpA = zeros(numA,3*dim);
helpB = zeros(numB,3*dim);
for idx = 1:dim
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*bigList(:,idx), bigList(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [littleList(:,idx).^2 ,    littleList(:,idx), ones(numB,1)];
end
[~,minIdx] = min(helpA * helpB',[],2); %//'# minIdx is what you are after
基准结果-

N1 = 1750; N2 = 4*N1; %/ datasize
littleList = rand(N1, 3);
bigList = rand(N2, 3);

for k = 1:50000
    tic(); elapsed = toc(); %// Warm up tic/toc
end

disp('------------- With squeeze + bsxfun + permute based approach [LuisMendo]')
tic
d = squeeze(sum((bsxfun(@minus, bigList, permute(littleList, [3 2 1]))).^2, 2));
[~, ind] = min(d,[],2);
toc,  clear d ind

disp('------------- With double permutes + bsxfun based approach [Shai]')
tic
d = bsxfun( @minus, permute( bigList, [1 3 2] ), permute( littleList, [3 1 2] ) ); %//diff in third dimension
d = sum( d.^2, 3 ); %// sq euclidean distance
[~,minIdx] = min( d, [], 2 );
toc
clear d minIdx

disp('------------- With bsxfun + matrix-multiplication based approach [Shai]')
tic
nb = sum( bigList.^2, 2 ); %// norm of bigList's items
nl = sum( littleList.^2, 2 ); %// norm of littleList's items
d = bsxfun(@plus, nb, nl.' ) - 2 * bigList * littleList'; %// all the distances
[~,minIdx] = min(d,[],2);
toc, clear nb nl d minIdx

disp('------------- With matrix multiplication based approach  [Divakar]')
tic
dim = 3;
numA = size(bigList,1);
numB = size(littleList,1);

helpA = zeros(numA,3*dim);
helpB = zeros(numB,3*dim);
for idx = 1:dim
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*bigList(:,idx), bigList(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [littleList(:,idx).^2 ,    littleList(:,idx), ones(numB,1)];
end
[~,minIdx] = min(helpA * helpB',[],2);
toc, clear dim numA numB helpA helpB idx minIdx

disp('------------- With pdist2 based approach [Divakar]')
tic
[~,minIdx] = min(pdist2(bigList,littleList),[],2);
toc, clear minIdx

快速结论:Shai的第二种方法是结合
bsxfun
和矩阵乘法的运行时与基于
pdist2
的运行时非常接近,无法在这两种方法之间确定明确的赢家。

你有统计工具箱吗,因为…@RobDonnelly假设你会这样做零填充你的
小列表
?否则可能是:@LuisMendo don;我不知道你的想法,我的不太好;)但是谢谢!抱歉太厚了,只是想知道为什么你把你的
大列表
排列成
[1 3 2]
小列表
排列成
[3 1 2]
,而不是像@Luis solution?@kkuilla那样,要求和的维度是最后一个,这样就避免了
挤压
第二个看起来肯定更好@Divakar目前运行基准测试时遇到问题。您能将其与您的解决方案进行比较吗?与相比,这避免了一次
置换
,但需要一次
挤压
。另外,
sum(…,2)
通常比
sum(…,3)
快一点。矩阵乘法:想得好@这是一个很好的解决方案,只是在这里滥用:)
|| a - b ||^2 = ||a||^2 + ||b||^2 - 2<a,b> 
[~,minIdx] = min(pdist2(bigList,littleList),[],2); %// minIdx is what you are after
dim = 3;
numA = size(bigList,1);
numB = size(littleList,1);

helpA = zeros(numA,3*dim);
helpB = zeros(numB,3*dim);
for idx = 1:dim
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*bigList(:,idx), bigList(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [littleList(:,idx).^2 ,    littleList(:,idx), ones(numB,1)];
end
[~,minIdx] = min(helpA * helpB',[],2); %//'# minIdx is what you are after
N1 = 1750; N2 = 4*N1; %/ datasize
littleList = rand(N1, 3);
bigList = rand(N2, 3);

for k = 1:50000
    tic(); elapsed = toc(); %// Warm up tic/toc
end

disp('------------- With squeeze + bsxfun + permute based approach [LuisMendo]')
tic
d = squeeze(sum((bsxfun(@minus, bigList, permute(littleList, [3 2 1]))).^2, 2));
[~, ind] = min(d,[],2);
toc,  clear d ind

disp('------------- With double permutes + bsxfun based approach [Shai]')
tic
d = bsxfun( @minus, permute( bigList, [1 3 2] ), permute( littleList, [3 1 2] ) ); %//diff in third dimension
d = sum( d.^2, 3 ); %// sq euclidean distance
[~,minIdx] = min( d, [], 2 );
toc
clear d minIdx

disp('------------- With bsxfun + matrix-multiplication based approach [Shai]')
tic
nb = sum( bigList.^2, 2 ); %// norm of bigList's items
nl = sum( littleList.^2, 2 ); %// norm of littleList's items
d = bsxfun(@plus, nb, nl.' ) - 2 * bigList * littleList'; %// all the distances
[~,minIdx] = min(d,[],2);
toc, clear nb nl d minIdx

disp('------------- With matrix multiplication based approach  [Divakar]')
tic
dim = 3;
numA = size(bigList,1);
numB = size(littleList,1);

helpA = zeros(numA,3*dim);
helpB = zeros(numB,3*dim);
for idx = 1:dim
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*bigList(:,idx), bigList(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [littleList(:,idx).^2 ,    littleList(:,idx), ones(numB,1)];
end
[~,minIdx] = min(helpA * helpB',[],2);
toc, clear dim numA numB helpA helpB idx minIdx

disp('------------- With pdist2 based approach [Divakar]')
tic
[~,minIdx] = min(pdist2(bigList,littleList),[],2);
toc, clear minIdx
------------- With squeeze + bsxfun + permute based approach [LuisMendo]
Elapsed time is 0.718529 seconds.
------------- With double permutes + bsxfun based approach [Shai]
Elapsed time is 0.971690 seconds.
------------- With bsxfun + matrix-multiplication based approach [Shai]
Elapsed time is 0.328442 seconds.
------------- With matrix multiplication based approach  [Divakar]
Elapsed time is 0.159092 seconds.
------------- With pdist2 based approach [Divakar]
Elapsed time is 0.310850 seconds.