【问题标题】:Chose the farthest k points from given n points从给定的 n 个点中选择最远的 k 个点
【发布时间】:2013-10-07 20:47:36
【问题描述】:

我在维度 d 中有一组 n 个点,如果需要,我可以计算所有成对距离。我需要在这个集合中选择 k 个点,以使它们的成对距离之和最大。换句话说,稍微更数学的话,我希望 S 中的 p1, ..., pk 使得 sum(i,j

我知道这个问题与this one(这与我的基本相同,但 k=2 相同)和this one(用“最远”而不是“最近”)有关。

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

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

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

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

【问题讨论】:

  • Re:凸包上的所有解决方案点:不幸的是,这不是真的,因为凸包的顶点数可能少于k
  • 那么,如果k h,h是船体中的点数,是不是真的?
  • 通常“最远”/“最长”比“最近”更难用图表。 不要认为如果你能找到最近的点/最小值,那么你也可以使用几乎相同的代码和相同的时间找到最远的点/最大值。
  • 我问过类似的问题,得到了一些很好的答案:stackoverflow.com/questions/48925086/…。我的问题的不同之处在于我想优化距离的最小值而不是距离的总和。

标签: python algorithm numpy geometry


【解决方案1】:

您的问题似乎类似于weighted minimum vertex cover problem(这是 NP 完全问题)。感谢@Gareth Rees 下面的 cmets 澄清我在理解顶点覆盖与您正在寻找的集合的关系方面是不正确的。但是您可能仍然会研究顶点覆盖问题和文献,因为您的问题可能会与它一起讨论,因为它们仍然共享一些特征。

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

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

这似乎是一个组合难题。您可能会考虑一些简单的事情,例如模拟退火。提议函数可以随机选择当前在k-subset 中的点,然后用当前不在k-subset 中的点随机替换它。

您需要针对温度项制定良好的冷却计划,并且可能需要根据成本使用再加热。但是这种类型的编程真的很简单。只要n 相当小,您就可以不断地随机选择k-subsets 并退火到总距离非常大的k-subset。

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

下面是模拟退火代码的第一次破解。 请注意,我对此不做任何保证。如果计算距离太难或问题实例大小变得太大,这可能是一个低效的解决方案。我正在使用具有固定冷却速率的非常简单的几何冷却,您可能还想修改一个更奇特的建议,而不是仅仅在节点周围随机交换。

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

【讨论】:

  • 感谢您的建议。我在考虑蒙特卡罗算法,但我肯定会尝试这种模拟退火方法。我会等几个小时/几天,看看是否有更多“专业”解决方案出现,否则我想我会选择这个。
  • 至于采用d' = 1/d 的建议,我不太确定,因为1/d 对我来说似乎不是一个真正的“指标”,我不知道是什么样的这对提议的算法有影响。
  • 我不同意你的观点,即 OP 的问题是 NP 完全的,我也看不到与 MINIMUM VERTEX COVER 的联系。 (在 MINIMUM VERTEX COVER 问题中,您试图选择 最小的 覆盖集,但在 OP 问题中,您必须选择 exactly k 个点。)你能扩展这部分吗你的答案?
  • 我不知道是否存在完美的连接,这就是为什么我说“我不完全确定”。我也在谈论加权最小顶点覆盖。而且由于该图在任何两点之间必然有一条边(因为它与度量空间只有一段距离),我相信这可以避免您提到的问题。也就是说,任何 k 组点都将是一个覆盖,甚至单个点也可能是一个覆盖,所以剩下的就是权重最小化。我不知道这种简化是否可以在多时间内解决,它很容易像最大独立集的二分版本。
  • 如果一个图在每两个顶点之间都有一条边,那么一个顶点覆盖必须包含所有顶点,或者除一个之外的所有顶点。 (如果一个集合 S 缺少两个顶点,那么这两个顶点之间有一条未被覆盖的边,因此 S 不是顶点覆盖。)所以我不明白你的说法“因为图之间必然有一条边任何两个点......任何 k 组点都将是一个封面”。
【解决方案2】:

这个贪心算法怎么样:

  1. 将 S 中距离最大的 2 个点添加到解中
  2. 直到找到大小为 k 的解,向解中添加从它到解中已存在的所有点的距离总和最大的点。

让我们将任意 2 点之间的最大距离称为 D,对于我们添加到解中的每个点,由于三角不等式,我们至少添加 D。所以解决方案将至少为 (k-1)*D,而任何解决方案都将具有 (k-1)^2 距离,但它们都没有超过 D,所以在最坏的情况下,您会得到一个最佳解决方案的 k 倍。

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

【讨论】:

  • 这是一个很好的建议。
  • 虽然这是进一步优化的一个很好的起点,但我认为这种贪心算法存在一些问题。取一组大致形成圆盘的二维点,k=3。该算法在外圆上取 2 个属于相同直径的点,然后在圆上以 90 度角添加一个点,从而得到一个与最佳形状的等边三角形相距甚远的直角三角形。
  • @Nathan,问题是您所说的“离最佳形状还很远”是什么意思。您是否有一个特定的应用程序可以量化非最优性的容差?正如我所解释的那样,定位直角三角形的贪心算法是等边三角形的一个非常好的近似值,因为这是一种非常简单的方法。做出诸如“直角三角形不是等边三角形的良好近似”之类的不合格陈述是没有帮助的。具体来说,您所说的“良好近似”是什么意思?
  • 对不起,我认为这是一个相当主观的陈述。我想到了距离最优解的距离总和约为 0.1-1% 的误差。
  • 在修改了模拟退火方案之后,我必须承认这个贪心算法产生了非常好的结果,很难改进。不错!
【解决方案3】:

第 1 步:预先计算所有对 pi,pj 的 dist(pi, pj)

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

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

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

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

【讨论】:

    【解决方案4】:

    这是一个小 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)!)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 2010-12-23
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多