在给定的点集中选择最远点的子集

时间:2018-02-22 10:29:28

标签: python algorithm computational-geometry dimensionality-reduction multi-dimensional-scaling

想象一下,你给出了3个维度中n个点的集合S.任意2点之间的距离是简单的欧几里德距离。您希望从该集合中选择k个点的子集Q,使得它们彼此相距最远。换句话说,不存在k点的其他子集Q',使得Q中所有成对距离的min小于Q'中的min。

如果n约为1600万,k约为300,我们如何有效地做到这一点?

我的猜测是,这个NP难,所以我们可能只想关注近似。我能想到的一个想法是使用多维缩放对一行中的这些点进行排序,然后使用二进制搜索的版本来获得该行上最远的点。

4 个答案:

答案 0 :(得分:8)

这被称为离散p色散(maxmin)问题。

White (1991)中证明了最优边界,Ravi et al. (1994)给出了问题的因子2近似值,后者证明了这种启发式方法是最好的(除非P = NP)。

因子2逼近

因子2近似如下:

Let V be the set of nodes/objects
Let i and j be two nodes at maximum distance
Let p be the number of objects to choose
p = set([i,j])
while size(P)<p:
  Find a node v in V-P such that min_{v' in P} dist(v,v') is maximum
  \That is: find the node with the greatest minimum distance to the set P
  P = P.union(v)
Output P

您可以像这样在Python中实现这一点:

#!/usr/bin/env python3

import numpy as np

p = 50
N = 400

print("Building distance matrix...")
d = np.random.rand(N,N) #Random matrix
d = (d + d.T)/2         #Make the matrix symmetric

print("Finding initial edge...")
maxdist  = 0
bestpair = ()
for i in range(N):
  for j in range(i+1,N):
    if d[i,j]>maxdist:
      maxdist = d[i,j]
      bestpair = (i,j)

P = set()
P.add(bestpair[0])
P.add(bestpair[1])

print("Finding optimal set...")
while len(P)<p:
  print("P size = {0}".format(len(P)))
  maxdist = 0
  vbest = None
  for v in range(N):
    if v in P:
      continue
    for vprime in P:
      if d[v,vprime]>maxdist:
        maxdist = d[v,vprime]
        vbest   = v
  P.add(vbest)

print(P)

确切的解决方案

您也可以将此模型建模为MIP。对于6000秒后p = 50,n = 400的情况,最优缺口仍为568%。近似算法花了0.47s来获得100%(或更小)的最佳间隙。天真的Gurobi Python表示可能看起来像这样:

#!/usr/bin/env python
import numpy as np
import gurobipy as grb

p = 50
N = 400

print("Building distance matrix...")
d = np.random.rand(N,N) #Random matrix
d = (d + d.T)/2             #Make the matrix symmetric

m = grb.Model(name="MIP Model")

used  = [m.addVar(vtype=grb.GRB.BINARY) for i in range(N)]

objective = grb.quicksum( d[i,j]*used[i]*used[j] for i in range(0,N) for j in range(i+1,N) )

m.addConstr(
  lhs=grb.quicksum(used),
  sense=grb.GRB.EQUAL,
  rhs=p
)

# for maximization
m.ModelSense = grb.GRB.MAXIMIZE
m.setObjective(objective)

# m.Params.TimeLimit = 3*60

# solving with Glpk
ret = m.optimize()

缩放

显然,初始点的O(N ^ 2)缩放比例不好。通过识别该对必须位于数据集的凸包上,我们可以更有效地找到它们。这为我们提供了一种 O(N log N)查找对的方法。找到它后,我们将像以前一样进行操作(使用SciPy进行加速)。

最好的缩放方式是使用R *-树有效地找到候选点p和集合P之间的最小距离。但是,这在Python中无法高效完成,因为for循环是仍然参与其中。

import numpy as np
from scipy.spatial import ConvexHull
from scipy.spatial.distance import cdist

p = 300
N = 16000000

# Find a convex hull in O(N log N)
points = np.random.rand(N, 3)   # N random points in 3-D

# Returned 420 points in testing
hull = ConvexHull(points)

# Extract the points forming the hull
hullpoints = points[hull.vertices,:]

# Naive way of finding the best pair in O(H^2) time if H is number of points on
# hull
hdist = cdist(hullpoints, hullpoints, metric='euclidean')

# Get the farthest apart points
bestpair = np.unravel_index(hdist.argmax(), hdist.shape)

P = np.array([hullpoints[bestpair[0]],hullpoints[bestpair[1]]])

# Now we have a problem
print("Finding optimal set...")
while len(P)<p:
  print("P size = {0}".format(len(P)))
  distance_to_P        = cdist(points, P)
  minimum_to_each_of_P = np.min(distance_to_P, axis=1)
  best_new_point_idx   = np.argmax(minimum_to_each_of_P)
  best_new_point = np.expand_dims(points[best_new_point_idx,:],0)
  P = np.append(P,best_new_point,axis=0)

print(P)

答案 1 :(得分:3)

我也非常确定问题是NP-Hard,我发现最类似的问题是k-Center Problem。如果运行时比正确性更重要,那么贪婪算法可能是您的最佳选择:

Q ={}
while |Q| < k
    Q += p from S where mindist(p, Q) is maximal

旁注:在类似的问题中,例如,set-cover problem可以证明贪婪算法的解决方案至少比最优解决方案好63%。

为了加快速度,我看到了3种可能性:

  1. 首先在R-Tree中对数据集进行索引,然后执行贪婪搜索。 R-Tree的构造是O(n log n),但是虽然是为最近邻搜索而开发的,但它也可以帮助您找到O(log n)中一组点的最远点。这可能比天真的O(k * n)算法更快。

  2. 从1600万个点中抽取一个子集,并对子集执行贪婪算法。无论如何,你都是近似的,所以你可能会更加准确。您还可以将其与1.算法结合使用。

  3. 使用迭代方法,当您没有时间时停止。这里的想法是从S中随机选择k个点(让我们称之为Q')。然后在每个步骤中,将点p_从具有最小距离的Q'切换到Q'中的另一个距离,从S开始随机点。如果得到的集Q''更好,则继续Q'',否则重复Q' 。为了不被卡住你可能想要从Q'中选择另一个点而不是p_如果你找不到足够的替代品进行几次迭代。

答案 2 :(得分:0)

如果你能负担得起~k * n距离计算,那么你可以

  1. 找到分布点的中心。
  2. 选择距离中心最远的点。 (并将其从未选择的点集中删除)。
  3. 找到距离所有当前所选点最远的点并选择它。
  4. 重复3.直到你以k点结束。

答案 3 :(得分:0)

找出所有点的最大范围。分割为7x7x7体素。对于体素中的所有点,请找到最靠近其中心的点。返回这些7x7x7点。一些体素可能不包含点,希望不会太多。