【问题标题】:Given two sets of vectors, how do I find the closest vector in the second set for each vector in the first set?给定两组向量,如何为第一组中的每个向量找到第二组中最接近的向量?
【发布时间】:2016-02-13 03:06:19
【问题描述】:

给定:两组{S1, S2} 维度为D 的向量。 S1N*D 矩阵表示,因此S2M*D 矩阵表示。

我正在寻找一种优雅的方法来获取S1 中的每个向量s1S2 中最近的邻居s2 和相应的距离。

一个简单的方法当然是有两个for循环并得到

dist = norm(s1 - s2);

但是,必须有一种更优雅、更有效的方法来做到这一点。

【问题讨论】:

    标签: matlab vector cluster-analysis nearest-neighbor


    【解决方案1】:

    是的。拥有bsxfunpermute 的强大力量,有sum 的一面,还有reshape 的破折号。这是第一部分,您将计算S1 中的一个点与S2 中的另一个点之间的成对距离:

    out = reshape(sqrt(sum(bsxfun(@minus, S1, permute(S2, [3 2 1])).^2, 2)), size(S1,1), size(S2,1));
    

    您现在需要做的最后一件事是确定S2 中与每个S1 最接近的向量。这可以使用min 来完成:

    [dist,ind] = min(out, [], 2);
    

    dist 将包含S1 中的点与S2 中的点之间的最小距离,ind 会告诉您那是哪个点。


    这段代码看起来很吓人,但让我们把它分解成小块。

    1. permute(S2, [3 2 1]):这采用矩阵S2,它是一个M x D 矩阵并打乱维度使其成为1 x D x M 矩阵......现在我们为什么要这样做?让我们进入下一部分,它会更有意义。

    2. bsxfun(@minus, S1, ...)bsxfun 代表 Binary Singleton EXpansion FUNction。 bsxfun 所做的是,如果您有两个输入,其中一个或两个输入都具有单个维度,或者如果两个输入中的任何一个只有一个值为 1 的维度,则每个输入都在其单个维度中复制以匹配大小另一个输入,然后将逐元素操作应用于这些输入以产生您的输出。在这种情况下,我想将这两个新形成的输入相减。

      因此,鉴于S1N x D...或者从技术上讲,这是N x D x 1,并且鉴于S2M x D,我将其置换为1 x D x M,我们将创建一个长度为N x D x M 的新矩阵。第一个输入将自身复制为一个 3D 矩阵,其中每个切片等于S1,即N x DS2 现在是一个 3D 矩阵,但它的表示方式是原始矩阵中的每一行都是 3D 矩阵中的一个切片,其中每个切片仅包含一行。这会为 N 行重复。

      我们现在应用@minus 操作,其效果是对于这个新矩阵中的每个输出切片i,这为您提供了S2 中的点i 与所有S1 中的其他点。例如,对于切片#1,行#1 为您提供S2 中的点#1 和S1 中的点#1 之间的组件差异。第 2 行为您提供了 S2 中的第 1 点和 S1 中的第 2 点之间的组件差异,依此类推。

    3. sum((...).^2, 2):我们想找到一个点和另一个点之间的欧几里得距离,所以我们将这些距离在每一列上独立求和。这会产生一个新的 3D 矩阵,其中每个切片包含 N 值,其中每个 M 点都有 N 距离。例如,第一个切片将为您提供从 S2 中的点 #1 到 S1 中的所有其他点的距离。

    4. out = reshape(..., size(S1,1), size(S2,1));:我们现在对其进行整形,使其成为M x N 矩阵,以便(i,j) 的每一行和列对为您提供S1 和@987654379 中i 点之间的距离@ in S2,从而完成计算。

    5. 执行[dist,ind] = min(out, [], 2); 确定S1 中的一个点与S2 中的其他点之间的最小距离。 dist 会告诉你最小的距离,而ind 会告诉你它是哪个向量。因此,对于dist 中的每个元素,它会为您提供S1 中的点iS2 之一之间的最小距离,而ind 会告诉您哪个向量属于S2


    我们可以通过使用您提出的遍历每对点并计算范数的方法来验证这是否为我们提供了正确的结果。让我们创建S1S2

    S1 = [1 2 3; 4 5 6; 7 8 9; 10 11 12];
    S2 = [-1 -1 -1; 0 9 8];
    

    展示更整齐:

    >> S1
    
    S1 =
    
         1     2     3
         4     5     6
         7     8     9
        10    11    12
    
    >> S2
    
    S2 =
    
        -1    -1    -1
         0     9     8
    

    使用循环方法,我们有以下代码:

    out = zeros(size(S1,1), size(S2,1));
    for ii = 1 : size(S1,1)
        for jj = 1 :size(S2,1) 
            out(ii,jj) = norm(S1(ii,:) - S2(jj,:));
        end
    end
    

    我们得到这个矩阵:

    >> out
    
    out =
    
        5.3852    8.6603
       10.4881    6.0000
       15.6525    7.1414
       20.8327   10.9545
    

    同样,如果我们运行我编写的代码,我们也会得到:

    >> out = reshape(sqrt(sum(bsxfun(@minus, S1, permute(S2, [3 2 1])).^2, 2)), size(S1,1), size(S2,1))
    
    out =
    
        5.3852    8.6603
       10.4881    6.0000
       15.6525    7.1414
       20.8327   10.9545
    

    为了完成这个过程,让我们找到最小的距离和对应的向量:

    >> [dist,ind] = min(out, [], 2);
    >> dist
    
    dist =
    
        5.3852
        6.0000
        7.1414
       10.9545
    
    >> ind
    
    ind =
    
         1
         2
         2
         2
    

    因此,对于S1 中的第一个向量,S2 中与此最近的向量是第一个向量,距离为 5.3852。同样,S1 的第二个向量,S2 中最接近的向量是第二个,距离为 6。您可以对其他值重复此操作,看看是否正确。

    【讨论】:

    • 这并没有超出计算 N*M 范数,对吗?它只是摆脱了 for 循环?
    • @MatthewGunn 不,它没有,但它更快。
    • 我之前写的都是错的。如果有好的方法,让我考虑一下。
    • @MatthewGunn 完全没有问题。我很想知道如何使用矩阵乘法来做到这一点。我是这样开始的,但找不到办法,这就是我退出bsxfun的原因。
    • 某种迭代方式,无论是 for 循环还是 bsxfun 都是获取每个组合的向量差异的方法。对不起我之前的放屁。
    【解决方案2】:

    一行怎么样?

    [dist, ind] = min(pdist2(S2,S1));
    

    indS2中每个向量在S1中的最近向量的索引,dist给出了对应的最小距离。

    借用@rayryeng's example

    S1 = [1 2 3; 4 5 6; 7 8 9; 10 11 12];
    S2 = [-1 -1 -1; 0 9 8];
    

    结果是

    dist =
       5.385164807134504   6.000000000000000   7.141428428542850  10.954451150103322
    ind =
        1     2     2     2
    

    【讨论】:

    • 啧啧,是不是也比bsxfunbsxfun快了?
    • @Adriaan Nothingbsxfun 快 :-P 除了 matrix multiplication
    • 非常好。我忘了使用pdist2,但我喜欢我自己想出的bsxfun/permute
    猜你喜欢
    • 2022-08-14
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    相关资源
    最近更新 更多