从给定的n点选择最远的k点

时间:2013-10-01 15:41:54

标签: python algorithm numpy geometry

我在维度 d 中有一组 n 点,我可以根据需要计算所有成对距离。我需要在此集合中选择 k 点,以便它们成对距离的总和最大。在其他稍微更多的数学词中,我想要S中的p1,...,pk使得sum(i,j

我知道这个问题与this one有关(这与我的基本相同,但对于k = 2),也许与this one有关(与'最远'而不是'最近')。< / p>

我对此并不太确定,但也许所有可能的解决方案都有其在凸包中的所有点?

任何合理的近似/启发式都可以。

一个解决方案的虚拟奖励积分#1,该解决方案适用于给出四个点中的分数的任何函数(其中一个可以是平方距离之和的平方根)。

虚拟奖励积分#2如果解决方案很容易在python + numpy / scipy中实现。

4 个答案:

答案 0 :(得分:6)

您的问题似乎与weighted minimum vertex cover problem(NP-complete)相似。感谢@Gareth Rees对以下评论的澄清,说明我在理解顶点封面与您正在寻找的集合之间的关系时是错误的。但你仍然可以研究顶点覆盖问题和文献,因为你的问题可能会与它一起讨论,因为它们仍然可以分享一些特征。

如果您愿意使用直径而不是求和图形权重,则可以将该方法用于您在问题中链接的最小直径集。如果您当前的距离度量被称为d(您想要彼此距离最远的那个),那么只需定义d' = 1/d并使用d'解决最小距离问题。

某些形式的图切割算法(例如说normalized cut)与您寻找的子集之间可能存在关联。如果您的距离度量用作节点之间的图形权重或亲和力,您可以修改现有的图形切割目标函数以匹配您的目标函数(查找具有最大总和权重的k个节点组)。

这似乎是一个组合难题。您可能会考虑像模拟退火一样简单的事情提案函数可以随意选择当前位于k - 子集中的点,并随机替换为当前不在k - 子集中的点。

您需要一个良好的温度计划冷却时间表,可能需要使用再加热作为成本的函数。但是这种方法非常简单。只要n相当小,您就可以随时随机选择k - 子集并退回到k - 具有非常大的总距离的子集。

这只会给你一个近似值,但即使是确定性方法也可能会解决这个问题。

下面是关于模拟退火代码可能是什么的第一个黑客。 注意我不保证这一点。如果计算距离太大或问题实例大小变得太大,则可能是低效的解决方案。我正在使用具有固定冷却速率的非常天真的几何冷却,你可能还想修改一个更好的提议,而不是随意地在节点周围交换。

all_nodes = np.asarray(...) # Set of nodes
all_dists = np.asarray(...) # Pairwise distances

N = len(all_nodes)
k = 10 # Or however many you want.

def calculate_distance(node_subset, distances):
    # A function you write to determine sum of distances
    # among a particular subset of nodes.    

# Initial random subset of k elements
shuffle = np.random.shuffle(all_nodes) 
current_subset = shuffle[0:k]
current_outsiders = shuffle[k:]

# Simulated annealing parameters.
temp = 100.0
cooling_rate = 0.95
num_iters = 10000

# Simulated annealing loop.
for ii in range(num_iters):
    proposed_subset = current_subset.copy()
    proposed_outsiders =  current_outsiders.copy()

    index_to_swap = np.random.randint(k)
    outsider_to_swap = np.random.randint(N - k)

    tmp = current_subset[index_to_swap]
    proposed_subset[index_to_swap] = current_outsiders[outsider_to_swap]
    proposed_outsiders[outsider_to_swap] = tmp

    potential_change = np.exp((-1.0/temp)*
        calculate_distance(proposed_subset,all_dists)/
        calculate_distance(current_subset, all_dists)) 

    if potential_change > 1 or potential_change >= np.random.rand():
         current_subset = proposed_subset
         current_outsiders = proposed_outsiders

    temp = cooling_rate * temp

答案 1 :(得分:6)

这个贪心算法怎么样:

  1. 在S
  2. 中添加2点,它们之间的距离最大
  3. 直到您达到大小为k的解决方案,向解决方案添加从其到解决方案中已有的所有点的距离之和最大的点。
  4. 让我们调用任意2点D之间的最大距离,对于我们添加到解决方案的每个点,由于三角形不等式,我们至少添加D.所以解决方案至少是(k-1)* D,而任何解决方案都有(k-1)^ 2个距离,它们都不超过D,所以在更糟糕的情况下,你会得到k倍于最优值的解。 / p>

    我不确定这是否可以证明这种启发式的最严格的界限。

答案 2 :(得分:2)

步骤1:对所有对pi,pj

预先计算dist(pi,pj)

步骤2:构造一个完整的图形V = {p1,...,pn},边权重为w_ij = dist(pi,pj)

步骤3:解决最大边缘加权团(MEC)问题。

MEC绝对是NP完全的,它与二次规划密切相关。因此,如果n很大(甚至是中等大小),您可能会专注于启发式。

请注意,通过预先计算边权重,对距离函数没有限制

答案 3 :(得分:0)

这是一个小n的工作(暴力)实现,如果没有其他任何东西显示生成器理解的表现力:

selection = max(
    itertools.combinations(points, k),
    key=lambda candidate: sum(
        dist(p, q) for p, q in itertools.combinations(candidate, 2)
    )
)

虽然这最终会调用dist

(k! / 2! / (k-2)!) * (n! / k! / (n-k)! == n! /(2(k-2)!(n-k)!)