【问题标题】:minimum spanning tree remove all extra edges最小生成树删除所有额外的边
【发布时间】:2021-12-14 13:11:57
【问题描述】:

额外的边——由 2 个点组成的边,每个点都与另一条边相连。 我想通过删除这些边缘来断开 MST。 什么是最小化新断开 MST 重量的最佳方法, 或者我应该以什么顺序删除这些边缘(删除一个可能会影响另一个)? 我的做法是先删除权重最大的多余边?

https://prnt.sc/1xq1msp

在这种情况下,删除 7(CD)-> 不能删除更多的边。 但你也可以删除 B-C,然后删除 D-E,这是更好的解决方案

【问题讨论】:

  • 你的问题有点难以理解。您是否正在寻找解决minimum-weight spanning tree problem 的算法?
  • 我举了一个例子,我希望现在更容易。
  • 它不必是 twobytwo,它必须是 bi 最小权重断开 mst,条件是每个点必须至少连接一次!因此,如果边中的两个点都与另一个边相连,则没有理由存在边,因为删除它们仍然是两个点都保持连接。
  • 你是对的,对不起。所以你正在寻找minimum edge cover
  • Murty 和 Perin 的论文“边缘覆盖问题的 1 匹配开花型算法”为您的最小和最大成本边缘问题提供了基于 O(n^3) 开花的解决方案盖子。它还包含在网络编程Murty's book 的第 10 章中,以及相关边缘覆盖和匹配问题的解决方案。

标签: python algorithm optimization minimum-spanning-tree


【解决方案1】:

这是一个使用 kd-tree 的 NumPy/SciPy/OR-Tools 的精确解决方案 枚举可能包含在其中的稀疏边子集 一个最优解,然后制定并求解一个混合整数规划。 但不确定它是否会满足您的需求;你可以设置一个间隙 如果您愿意接受近似值,请限制。

import collections
import numpy
import scipy.spatial

from ortools.linear_solver import pywraplp


def min_edge_cover(points):
    # Enumerate the candidate edges.
    candidate_edges = set()
    tree = scipy.spatial.KDTree(points)
    min_distances = numpy.ndarray(len(points))
    for i, p in enumerate(points):
        if i % 1000 == 0:
            print(i)
        distances, indexes = tree.query(p, k=2)
        # Ignore p itself.
        d, j = (
            (distances[1], indexes[1])
            if indexes[0] == i
            else (distances[0], indexes[0])
        )
        candidate_edges.add((min(i, j), max(i, j)))
        min_distances[i] = d
    for i, p in enumerate(points):
        if i % 1000 == 0:
            print(i)
        # An edge is profitable only if it's shorter than the sum of the
        # distance from each of its endpoints to that endpoint's nearest
        # neighbor.
        indexes = tree.query_ball_point(p, 2 * min_distances[i])
        for j in indexes:
            if i == j:
                continue
            discount = (
                min_distances[i] + min_distances[j]
            ) - scipy.spatial.distance.euclidean(points[i], points[j])
            if discount >= 0:
                candidate_edges.add((min(i, j), max(i, j)))
    candidate_edges = sorted(candidate_edges)

    # Formulate and solve a mixed integer program to find the minimum distance
    # edge cover. There's a way to do this with general weighted matching, but
    # OR-Tools doesn't expose that library yet.
    solver = pywraplp.Solver.CreateSolver("SCIP")
    objective = 0
    edge_variables = []
    coverage = collections.defaultdict(lambda: 0)
    for i, j in candidate_edges:
        x = solver.BoolVar("x{}_{}".format(i, j))
        objective += scipy.spatial.distance.euclidean(points[i], points[j]) * x
        coverage[i] += x
        coverage[j] += x
        edge_variables.append(x)
    solver.Minimize(objective)
    for c in coverage.values():
        solver.Add(c >= 1)
    solver.EnableOutput()
    assert solver.Solve() == pywraplp.Solver.OPTIMAL
    return {e for (e, x) in zip(candidate_edges, edge_variables) if x.solution_value()}


def random_point():
    return complex(random(), random())


def test(points, graphics=False):
    cover = min_edge_cover(points)
    if not graphics:
        return
    with open("out.ps", "w") as f:
        print("%!PS", file=f)
        print(0, "setlinewidth", file=f)
        inch = 72
        scale = 7 * inch
        print((8.5 * inch - scale) / 2, (11 * inch - scale) / 2, "translate", file=f)
        for x, y in points:
            print(scale * x, scale * y, 1, 0, 360, "arc", "fill", file=f)
        for i, j in cover:
            xi, yi = points[i]
            xj, yj = points[j]
            print(
                scale * xi,
                scale * yi,
                "moveto",
                scale * xj,
                scale * yj,
                "lineto",
                file=f,
            )
        print("stroke", file=f)
        print("showpage", file=f)


test(numpy.random.rand(100, 2), graphics=True)
test(numpy.random.rand(10000, 2))

【讨论】:

  • 太棒了。在 func min_edge_cover 中,在第一个 for 循环中,最后一行。难道不应该有min_distances[i] = distances[1],因为distances[0] = 0,与自己的距离
  • @PythonNewbie 呃,是的,这是一个错误。不幸的是,它使 MIP 变得微不足道,而且 MIP 现在明显变慢了。但我应用了上面的修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 2011-02-25
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多