【问题标题】:Implementation of Minimum Vertex Cover in a Bipartite Graph二部图中最小顶点覆盖的实现
【发布时间】:2021-10-26 22:42:00
【问题描述】:

我有一个相当大的二分图(每个部分约 200 个顶点,通常中间有 20,000 条或更多边),我试图在其中找到一个最小顶点覆盖,因为我正在寻找一个两个部分的顶点。

根据 Koenig 定理,存在与最大匹配 (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)) 的基数相同大小的覆盖。

我已经实现了 Hopcroft Karp 算法,它给了我一个最大匹配。如果需要,我可以提供我的实现,但我怀疑这就是我的问题所在。

真正的问题是什么?
我怀疑我的实现,取自上面的维基百科文章 (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)#Constructive_proof),其中有错误,但经过几个小时的检查后,我无法找到错误的原因:虽然 Hopcroft Karp 算法找到了最大匹配对于 192 个边,最小顶点覆盖返回 200 个顶点。由于这是一个二分图,因此这些数字不应该不同(因为定理)。也许你可以帮助我并告诉我我的错误在哪里。提前致谢!!

Student's 和Project's 是我在二分图中的两种类型的顶点)

internal static List<Vertex> FindMinimumVertexCover(IReadOnlyList<Edge> matching, IReadOnlyList<Vertex> studentVertices, IReadOnlyList<Vertex> projectVertices)
    {
        var unmatchedStudentNodes = studentVertices.Except(matching.Select(e => e.GetStudentVertex())).ToList();
        var visitedVertices = new List<Vertex>();
        var edgeComparer = new EdgeComparer();

        foreach (var unmatchedStudentNode in unmatchedStudentNodes)
        {
            visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, unmatchedStudentNode, visitedVertices, edgeComparer)).ToList();
        }

        visitedVertices = unmatchedStudentNodes.Union(visitedVertices).ToList();

        return studentVertices.Except(visitedVertices).Union(projectVertices.Intersect(visitedVertices)).ToList();
    }

private static List<Vertex> FindAlternatingNodes(IReadOnlyList<Edge> matching, Vertex initialVertex, List<Vertex> visitedVertices, EdgeComparer edgeComparer)
    {
        if (visitedVertices.Contains(initialVertex))
            return Enumerable.Empty<Vertex>().ToList();

        visitedVertices.Add(initialVertex);
        List<Edge> unmatchedEdges = initialVertex.Edges.Except(matching, edgeComparer).ToList();

        foreach (Edge unmatchedEdge in unmatchedEdges)
        {
            Vertex visitedVertex = unmatchedEdge.GetProjectVertex();
            Edge matchedEdge = matching.SingleOrDefault(e => e.GetProjectVertex().Equals(visitedVertex));

            if (matchedEdge != default(Edge))
            {
                visitedVertices.Add(visitedVertex);
                visitedVertex = matchedEdge.GetStudentVertex();
                visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, visitedVertex, visitedVertices, edgeComparer)).ToList();
            }
        }

        return visitedVertices;
    }

class EdgeComparer : IEqualityComparer<Edge>
{
    public bool Equals(Edge x, Edge y)
    {
        if (Object.ReferenceEquals(x, y))
            return true;

        if (x is null || y is null)
            return false;

        return Object.ReferenceEquals(x.GetStudentVertex(), y.GetStudentVertex()) && Object.ReferenceEquals(x.GetProjectVertex(), y.GetProjectVertex());
    }

    public int GetHashCode(Edge edge)
    {
        return (Student: edge.GetStudentVertex(), Project: edge.GetProjectVertex()).GetHashCode();
    }
}

【问题讨论】:

  • 很容易验证匹配(不一定是最大值),很容易验证顶点覆盖(不一定是最小值),并且很容易验证它们是否具有相同的大小(因此两者都被 LP 优化双重性)。这意味着您可以对小型随机图进行全自动测试,我保证从经验中可以解决问题。然后,您可以选择一个重现错误的图表,手动解决它,并应用标准的调试技术。
  • 似乎是一个有趣的问题,但需要minimal reproducible example。我怀疑你可以用更小的图表重现这个问题。假设您尝试的图形每个部分有 200 个顶点,那么我的猜测是该算法只是在一个部分中返回 all 的顶点。这将保证覆盖范围,因为所有边缘都在两个部分之间。

标签: c# algorithm graph-algorithm


【解决方案1】:

我现在发现了问题。我要感谢@David Eisenstat,因为他建议重复生成小的随机图。

问题出在我对 Vertex 类的实现中。

每次我创建 Edge 类的实例时,我都会将该 Edge 添加到相应的顶点(这意味着我有效地获得了 3 个对边的引用)。再次调用外部算法(调用上面的方法)只重新创建了边列表,但保留了顶点中的旧引用。因此,后续调用不会重新开始,最小顶点覆盖在图中发现不再存在的边(即List&lt;Edge&gt; unmatchedEdges = initialVertex.Edges.Except(matching, edgeComparer).ToList(); 线)。

【讨论】:

    猜你喜欢
    • 2017-08-07
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 2013-09-04
    • 2015-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多