在列表中查找彼此最接近的点的索引
Find the index of points closest to each other in a list
为了找到最近点,我一直无法实现该功能,我已经尝试了多种方法,但我似乎无法弄清楚。关于我应该如何解决它有什么想法吗?
欧氏距离
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = sqrt((x1-x2)**2 + (y1-y2)**2)
return dis
函数
def offices_to_merge(points):
min_p1 = 0
min_p2 = 1
for i in range(len(points)):
for j in range(i+1, len(points)):
dis = dist(points[i], points[j])
if dis < min((dis)) :
min_p1 = i
min_p2 = j
return (min_p1, min_p2)
>>> points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
>>> offices_to_merge(points)
(2, 4)
您可以使用 cdist
获取所有 points
:
之间的所有距离
from scipy.spatial.distance import cdist
import numpy as np
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
# calculate all distances between two sets of points
dists = cdist(points, points)
# the self distance is 0 -> we don't want this so make it large
dists[dists == 0] = dists.max()
# get index of smallest distance
np.unravel_index(dists.argmin(), dists.shape)
>>> (2, 4)
您的代码存在的问题是,您没有跟踪遍历点时观察到的当前最小距离:
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = ((x1-x2)**2 + (y1-y2)**2)**0.5
return dis
def offices_to_merge(points):
current_minimum = float('inf')
min_p1 = -1
min_p2 = -1
for i in range(len(points)):
for j in range(i+1, len(points)):
dis = dist(points[i], points[j])
if dis < current_minimum:
min_p1 = i
min_p2 = j
current_minimum = dis
return (min_p1, min_p2)
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
print( offices_to_merge(points) )
打印:
(2, 4)
您可以使用组合来查看所有可能的位置对。然后计算所有距离,取最小值,并确定产生该最小值的对的索引。
from numpy import sqrt
from itertools import combinations
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = sqrt((x1-x2)**2 + (y1-y2)**2)
return dis
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
a = list(combinations(points, 2)) # combinations
b = [dist(el1,el2) for el1,el2 in a] # distances
idx = b.index(min(b)) # index of the min
print(a[idx])
如果您的点列表很大,将具有 O(N^2) 时间复杂度的每个点配对的蛮力方法将很快成为性能瓶颈。
有一种方法可以在 O(NlogN) 时间内获得结果,方法是根据点到小于所有其他点(即下方和左侧)的任意基点的距离对点进行排序。使用这种排序方法,点的配对可以仅限于那些在目前发现的最短距离范围内的点。
这是一个例子:
def dist(a,b): return ((a[0]-b[0])**2 + (a[1]-b[1])**2)**0.5
def nearest2(points):
minP1,minP2 = points[:2]
minDist = dist(minP1,minP2)
base = tuple(map(min,zip(*points)))
sPoints = sorted((dist(base,p),p) for p in points)
iMin = 0
for ix,(xDist,px) in enumerate(sPoints[1:],1):
for i,(iDist,pi) in enumerate(sPoints[iMin:ix],iMin):
if iDist + minDist <= xDist: iMin = i+1; continue
if dist(px,pi) >= minDist: continue
minP1,minP2 = px,pi
minDist = dist(minP1,minP2)
return minP1,minP2
此函数将 return 比列表中任何其他点对更接近的两个点。 请注意,如果 dist() 函数是 3D 距离计算,则 nearest2() 函数适用于 3 维点列表
print(nearest2(points))
((200, 100), (150, 150))
为了进行比较,这是蛮力方法的样子(类似于您的函数):
def bruteForce(points):
minP1,minP2 = points[:2]
minDist = dist(minP1,minP2)
for i,p1 in enumerate(points[:-1]):
for p2 in points[i+1:]:
if dist(p1,p2) >= minDist: continue
minP1,minP2 = p1,p2
minDist = dist(minP1,minP2)
return minP1,minP2
测量性能差异(1000 分)说明了基于排序的方法的好处:
from random import randint
from timeit import timeit
count = 1
points = list(set( (randint(0,10000),randint(0,10000)*10) for _ in range(1000)))
t = timeit(lambda:nearest2(points),number=count)
print("nearest2 ",t) # 0.0022362289999999785
t = timeit(lambda:bruteForce(points),number=count)
print("bruteForce",t) # 0.36930638299999996
快了 150 多倍,而且随着你添加更多的点,差异会更大
如果您需要列表中的索引而不是点本身,您可以调整 nearest2() 函数或将其包装在一个函数中,该函数从得到的点对中找到索引:
def nearest2Index(points):
p1,p2 = nearest2(points) # bruteForce(points)
iP1 = points.index(p1)
iP2 = points.index(p2)
if iP1 == iP2: iP2 += points[iP1+1:].index(p2) + 1
if iP1>iP2: iP1,iP2 = iP2,iP1
return iP1,iP2
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
print(nearest2Index(points)) # (2,4)
为了找到最近点,我一直无法实现该功能,我已经尝试了多种方法,但我似乎无法弄清楚。关于我应该如何解决它有什么想法吗?
欧氏距离
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = sqrt((x1-x2)**2 + (y1-y2)**2)
return dis
函数
def offices_to_merge(points):
min_p1 = 0
min_p2 = 1
for i in range(len(points)):
for j in range(i+1, len(points)):
dis = dist(points[i], points[j])
if dis < min((dis)) :
min_p1 = i
min_p2 = j
return (min_p1, min_p2)
>>> points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
>>> offices_to_merge(points)
(2, 4)
您可以使用 cdist
获取所有 points
:
from scipy.spatial.distance import cdist
import numpy as np
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
# calculate all distances between two sets of points
dists = cdist(points, points)
# the self distance is 0 -> we don't want this so make it large
dists[dists == 0] = dists.max()
# get index of smallest distance
np.unravel_index(dists.argmin(), dists.shape)
>>> (2, 4)
您的代码存在的问题是,您没有跟踪遍历点时观察到的当前最小距离:
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = ((x1-x2)**2 + (y1-y2)**2)**0.5
return dis
def offices_to_merge(points):
current_minimum = float('inf')
min_p1 = -1
min_p2 = -1
for i in range(len(points)):
for j in range(i+1, len(points)):
dis = dist(points[i], points[j])
if dis < current_minimum:
min_p1 = i
min_p2 = j
current_minimum = dis
return (min_p1, min_p2)
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
print( offices_to_merge(points) )
打印:
(2, 4)
您可以使用组合来查看所有可能的位置对。然后计算所有距离,取最小值,并确定产生该最小值的对的索引。
from numpy import sqrt
from itertools import combinations
def dist(p1, p2):
x1, y1 = p1
x2, y2 = p2
dis = sqrt((x1-x2)**2 + (y1-y2)**2)
return dis
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
a = list(combinations(points, 2)) # combinations
b = [dist(el1,el2) for el1,el2 in a] # distances
idx = b.index(min(b)) # index of the min
print(a[idx])
如果您的点列表很大,将具有 O(N^2) 时间复杂度的每个点配对的蛮力方法将很快成为性能瓶颈。
有一种方法可以在 O(NlogN) 时间内获得结果,方法是根据点到小于所有其他点(即下方和左侧)的任意基点的距离对点进行排序。使用这种排序方法,点的配对可以仅限于那些在目前发现的最短距离范围内的点。
这是一个例子:
def dist(a,b): return ((a[0]-b[0])**2 + (a[1]-b[1])**2)**0.5
def nearest2(points):
minP1,minP2 = points[:2]
minDist = dist(minP1,minP2)
base = tuple(map(min,zip(*points)))
sPoints = sorted((dist(base,p),p) for p in points)
iMin = 0
for ix,(xDist,px) in enumerate(sPoints[1:],1):
for i,(iDist,pi) in enumerate(sPoints[iMin:ix],iMin):
if iDist + minDist <= xDist: iMin = i+1; continue
if dist(px,pi) >= minDist: continue
minP1,minP2 = px,pi
minDist = dist(minP1,minP2)
return minP1,minP2
此函数将 return 比列表中任何其他点对更接近的两个点。 请注意,如果 dist() 函数是 3D 距离计算,则 nearest2() 函数适用于 3 维点列表
print(nearest2(points))
((200, 100), (150, 150))
为了进行比较,这是蛮力方法的样子(类似于您的函数):
def bruteForce(points):
minP1,minP2 = points[:2]
minDist = dist(minP1,minP2)
for i,p1 in enumerate(points[:-1]):
for p2 in points[i+1:]:
if dist(p1,p2) >= minDist: continue
minP1,minP2 = p1,p2
minDist = dist(minP1,minP2)
return minP1,minP2
测量性能差异(1000 分)说明了基于排序的方法的好处:
from random import randint
from timeit import timeit
count = 1
points = list(set( (randint(0,10000),randint(0,10000)*10) for _ in range(1000)))
t = timeit(lambda:nearest2(points),number=count)
print("nearest2 ",t) # 0.0022362289999999785
t = timeit(lambda:bruteForce(points),number=count)
print("bruteForce",t) # 0.36930638299999996
快了 150 多倍,而且随着你添加更多的点,差异会更大
如果您需要列表中的索引而不是点本身,您可以调整 nearest2() 函数或将其包装在一个函数中,该函数从得到的点对中找到索引:
def nearest2Index(points):
p1,p2 = nearest2(points) # bruteForce(points)
iP1 = points.index(p1)
iP2 = points.index(p2)
if iP1 == iP2: iP2 += points[iP1+1:].index(p2) + 1
if iP1>iP2: iP1,iP2 = iP2,iP1
return iP1,iP2
points = [(350, 150), (500, 250), (150, 150), (50, 400), (200, 100)]
print(nearest2Index(points)) # (2,4)