我有一组点(x,y)。
我需要以最小距离返回两个点。
我用这个: http://www.cs.ucsb.edu/~suri/cs235/ClosestPair.pdf
但是,我真的不明白算法是如何运作的。
可以更简单地解释算法如何工作?
或提出另一个想法?
感谢!
答案 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)
魔法位于中位数或最短枝 - 从中位数到目前为止
感谢您提出这个问题,我不知道这个算法是如何工作的,但在幻灯片上找到了正确突出显示的子弹点并随之滚动。你现在明白了吗?我不知道如何解释除二分法搜索之外的中位数魔法是非常棒的。