找到点之间的最小距离

时间:2016-09-24 22:09:17

标签: algorithm math point

我有一组点(x,y)。

我需要以最小距离返回两个点。

我用这个: http://www.cs.ucsb.edu/~suri/cs235/ClosestPair.pdf

但是,我真的不明白算法是如何运作的。

可以更简单地解释算法如何工作?

或提出另一个想法?

感谢!

3 个答案:

答案 0 :(得分:1)

最近对的解决方案具有最小时间复杂度的问题 O(nlogn)是您在阅读过的文档中提到的分而治之的方法。

最近对问题的分治方法

理解这种算法的最简单方法是用高级语言(,因为有时理解算法或伪代码比理解实际代码更难)来阅读它的实现:

# closest pairs by divide and conquer
# David Eppstein, UC Irvine, 7 Mar 2002

from __future__ import generators

def closestpair(L):
    def square(x): return x*x
    def sqdist(p,q): return square(p[0]-q[0])+square(p[1]-q[1])

    # Work around ridiculous Python inability to change variables in outer scopes
    # by storing a list "best", where best[0] = smallest sqdist found so far and
    # best[1] = pair of points giving that value of sqdist.  Then best itself is never
    # changed, but its elements best[0] and best[1] can be.
    #
    # We use the pair L[0],L[1] as our initial guess at a small distance.
    best = [sqdist(L[0],L[1]), (L[0],L[1])]

    # check whether pair (p,q) forms a closer pair than one seen already
    def testpair(p,q):
        d = sqdist(p,q)
        if d < best[0]:
            best[0] = d
            best[1] = p,q

    # merge two sorted lists by y-coordinate
    def merge(A,B):
        i = 0
        j = 0
        while i < len(A) or j < len(B):
            if j >= len(B) or (i < len(A) and A[i][1] <= B[j][1]):
                yield A[i]
                i += 1
            else:
                yield B[j]
                j += 1

    # Find closest pair recursively; returns all points sorted by y coordinate
    def recur(L):
        if len(L) < 2:
            return L
        split = len(L)/2
        L = list(merge(recur(L[:split]), recur(L[split:])))

        # Find possible closest pair across split line
        # Note: this is not quite the same as the algorithm described in class, because
        # we use the global minimum distance found so far (best[0]), instead of
        # the best distance found within the recursive calls made by this call to recur().
        for i in range(len(E)):
            for j in range(1,8):
                if i+j < len(E):
                    testpair(E[i],E[i+j])
        return L

    L.sort()
    recur(L)
    return best[1]

closestpair([(0,0),(7,6),(2,20),(12,5),(16,16),(5,8),\
              (19,7),(14,22),(8,19),(7,29),(10,11),(1,13)])
# returns: (7,6),(5,8)

取自:https://www.ics.uci.edu/~eppstein/161/python/closestpair.py

详细说明:

首先,我们定义欧几里德距离又称方形距离函数,以防止代码重复。

def square(x): return x*x # Define square function
def sqdist(p,q): return square(p[0]-q[0])+square(p[1]-q[1]) # Define Euclidean distance function

然后我们将前两点作为我们最初的最佳猜测:

best = [sqdist(L[0],L[1]), (L[0],L[1])]

这是一个函数定义,用于比较下一对的欧几里德距离与当前最佳对:

def testpair(p,q):
    d = sqdist(p,q)
    if d < best[0]:
        best[0] = d
        best[1] = p,q

def merge(A,B):只是算法的倒带函数,用于合并先前分成一半的两个排序列表。

def recur(L):函数定义是算法的实际主体。所以我将更详细地解释这个函数定义:

    if len(L) < 2:
        return L

使用此部分,如果点列表中只剩下一个元素/点,算法将终止递归。

将列表拆分为一半:split = len(L)/2

为每一半创建一个递归(通过调用函数本身):L = list(merge(recur(L[:split]), recur(L[split:])))

然后最后这个嵌套循环将测试当前半列表中的整个对:

    for i in range(len(E)):
        for j in range(1,8):
            if i+j < len(E):
                testpair(E[i],E[i+j])

结果是,如果发现更好的一对,将更新最佳对。

答案 1 :(得分:1)

如果点数很少,你可以使用蛮力方法,即: 对于每个点,找到其他点中最近的点,并保存当前两个索引的最小距离,直到现在。

如果分数很大,我想你可以在这个帖子中找到答案: Shortest distance between points algorithm

答案 2 :(得分:0)

因此,他们使用分而治之的方法解决了许多方面的问题。二元搜索或分而治之是快速的。基本上,如果你可以将数据集拆分成两半,并继续这样做,直到你找到你想要的一些信息,你就可以在大多数时间内尽可能快地进行人工和计算机操作。

对于这个问题,这意味着我们将点数据集分成两组,S1和S2。

所有要点都是数字,对吧?所以我们必须选择一些数字来划分数据集。

所以我们选择一些数字 m 并说它是中位数。

让我们来看一个例子:

  

(14,2)
  (11,2)
  (5,2)
  (15,2)
  (0,2)

最近的一对是什么?

嗯,它们都有相同的Y坐标,所以我们只能看Xs ... X最短距离是14到15,距离是1.

我们怎样才能用分而治之的方式解决这个问题?

我们看X的最大值和X的最小值,我们选择中位数作为分隔线来制作我们的两组。

在这个例子中,我们的中位数是7.5。

然后我们制作2套

  

S1:(0,2)和(5,2)
  S2:(11,2)和(14,2)和(15,2)
  中位数:7.5

我们必须跟踪每次拆分的中位数,因为这实际上是此算法中至关重要的知识。他们没有在幻灯片上清楚地显示它,但是知道中值(你将一组分成两组)对于快速解决这个问题至关重要。

我们会在算法中跟踪他们称为 delta 的值。呃我不知道为什么大多数计算机科学家绝对会对命名变量感到厌烦,你需要在编码时使用描述性名称,这样你就不会忘记10年前编码的f000,所以不要使用delta#&# 39; s称这个值为我们最短的树枝 - 从中​​间到目前为止

由于我们的中值为7.5,所以我们分别看看我们最短的 - 从中​​间到最远的是针对Set1和Set2的:

  

Set1:最短 - 从中​​间到最远的 2.5(5到 m ,其中 m 是7.5)< / p>      

设置2:最短 - 从中​​间到最远 3.5(看11到 m

所以我认为从算法中得到的关键是,这个最短 - 从中​​间到最远的是你在每一次尝试改进的东西你划分一套的时间。

由于我们的案例中的S1只有2个元素,我们用左集合完成,右边的集合中有3个,所以我们继续划分:

  

S2 = {(11,2)(14,2)(15,2)}

你做什么的?你创建一个新的中位数,称之为 S2-median

S2-median 介于15到11 ......或13之间,对吧?我的数学可能很模糊,但我认为到目前为止是正确的。

所以,让我们看一下最短的树枝 - 为了我们的右侧 - 中间 - 十三 ...

  

15到13是... 2
  11到13是.... 2
  14到13是...... 1(!!!)

所以我们的 m 值或最短 - 从中​​间到目前为止的得到了改善(我们从之前更新了中位数,因为我们&#39;重新进入一个新的块或Set ...)

现在我们已经发现它,我们知道(14, 2)是满足最短对方程的点之一。然后,您可以针对此子集(15,11,14)中的点进行详尽检查,以查看哪一个更接近。

显然,在这种情况下,(15,2) and (14,2)是获胜对。

这有意义吗?您必须在剪切集合时跟踪中位数,并在每次剪切集合时保持新的中位数,直到每侧只剩下2个元素(或在我们的情况下为3)

魔法位于中位数或最短枝 - 从中​​位数到目前为止

感谢您提出这个问题,我不知道这个算法是如何工作的,但在幻灯片上找到了正确突出显示的子弹点并随之滚动。你现在明白了吗?我不知道如何解释除二分法搜索之外的中位数魔法是非常棒的。