📜  最小割的 Karger 算法 |第 1 套(介绍和实施)

📅  最后修改于: 2022-05-13 01:56:08.624000             🧑  作者: Mango

最小割的 Karger 算法 |第 1 套(介绍和实施)

给定一个无向且未加权的图,找到最小割(将图断开为两个组件的最小边数)。
输入图可能具有平行边。
例如考虑下面的例子,最小的切割有 2 条边。

卡格第一

一个简单的解决方案使用基于 Max-Flow 的 st 割算法来找到最小割。将每一对顶点视为源's'和汇't',并调用最小st割算法找到st割。返回所有 st 切割的最小值。该算法的最佳时间复杂度是图的 O(V 5 )。 [如何?总共有可能的 V 2对,并且一对的 st cut 算法需要 O(V*E) 时间和 E = O(V 2 )]。
下面是用于此目的的简单 Karger 算法。下面的 Karger 算法可以在 O(E) = O(V 2 ) 时间内实现。

1)  Initialize contracted graph CG as copy of original graph
2)  While there are more than 2 vertices.
      a) Pick a random edge (u, v) in the contracted graph.
      b) Merge (or contract) u and v into a single vertex (update 
         the contracted graph).
      c) Remove self-loops
3) Return cut represented by two vertices.

让我们通过给出的例子来理解上面的算法。
让第一个随机选择的顶点是连接顶点 0 和 1 的“ a ”。我们删除这条边并收缩图(合并顶点 0 和 1)。我们得到下图。

卡格2

让下一个随机选择的边缘为“d”。我们移除这条边并组合顶点 (0,1) 和 3。

卡格3

我们需要去除图中的自环。所以我们删除边缘'c'

卡格4

现在图有两个顶点,所以我们停下来。结果图中的边数是由 Karger 算法产生的切割。
Karger 算法是一种蒙特卡洛算法,它产生的割可能不是最小的。例如,下图显示了选取随机边的不同顺序会产生大小为 3 的最小切割。

卡格1

下面是上述算法的 C++ 实现。输入图表示为边的集合,并使用联合查找数据结构来跟踪组件。

C
// Karger's algorithm to find Minimum Cut in an
// undirected, unweighted and connected graph.
#include 
#include 
#include 
 
// a structure to represent a unweighted edge in graph
struct Edge
{
    int src, dest;
};
 
// a structure to represent a connected, undirected
// and unweighted graph as a collection of edges.
struct Graph
{
    // V-> Number of vertices, E-> Number of edges
    int V, E;
 
    // graph is represented as an array of edges.
    // Since the graph is undirected, the edge
    // from src to dest is also edge from dest
    // to src. Both are counted as 1 edge here.
    Edge* edge;
};
 
// A structure to represent a subset for union-find
struct subset
{
    int parent;
    int rank;
};
 
// Function prototypes for union-find (These functions are defined
// after kargerMinCut() )
int find(struct subset subsets[], int i);
void Union(struct subset subsets[], int x, int y);
 
// A very basic implementation of Karger's randomized
// algorithm for finding the minimum cut. Please note
// that Karger's algorithm is a Monte Carlo Randomized algo
// and the cut returned by the algorithm may not be
// minimum always
int kargerMinCut(struct Graph* graph)
{
    // Get data of given graph
    int V = graph->V, E = graph->E;
    Edge *edge = graph->edge;
 
    // Allocate memory for creating V subsets.
    struct subset *subsets = new subset[V];
 
    // Create V subsets with single elements
    for (int v = 0; v < V; ++v)
    {
        subsets[v].parent = v;
        subsets[v].rank = 0;
    }
 
    // Initially there are V vertices in
    // contracted graph
    int vertices = V;
 
    // Keep contracting vertices until there are
    // 2 vertices.
    while (vertices > 2)
    {
       // Pick a random edge
       int i = rand() % E;
 
       // Find vertices (or sets) of two corners
       // of current edge
       int subset1 = find(subsets, edge[i].src);
       int subset2 = find(subsets, edge[i].dest);
 
       // If two corners belong to same subset,
       // then no point considering this edge
       if (subset1 == subset2)
         continue;
 
       // Else contract the edge (or combine the
       // corners of edge into one vertex)
       else
       {
          printf("Contracting edge %d-%d\n",
                 edge[i].src, edge[i].dest);
          vertices--;
          Union(subsets, subset1, subset2);
       }
    }
 
    // Now we have two vertices (or subsets) left in
    // the contracted graph, so count the edges between
    // two components and return the count.
    int cutedges = 0;
    for (int i=0; i subsets[yroot].rank)
        subsets[yroot].parent = xroot;
 
    // If ranks are same, then make one as root and
    // increment its rank by one
    else
    {
        subsets[yroot].parent = xroot;
        subsets[xroot].rank++;
    }
}
 
// Creates a graph with V vertices and E edges
struct Graph* createGraph(int V, int E)
{
    Graph* graph = new Graph;
    graph->V = V;
    graph->E = E;
    graph->edge = new Edge[E];
    return graph;
}
 
// Driver program to test above functions
int main()
{
    /* Let us create following unweighted graph
        0------1
        | \    |
        |   \  |
        |     \|
        2------3   */
    int V = 4;  // Number of vertices in graph
    int E = 5;  // Number of edges in graph
    struct Graph* graph = createGraph(V, E);
 
    // add edge 0-1
    graph->edge[0].src = 0;
    graph->edge[0].dest = 1;
 
    // add edge 0-2
    graph->edge[1].src = 0;
    graph->edge[1].dest = 2;
 
    // add edge 0-3
    graph->edge[2].src = 0;
    graph->edge[2].dest = 3;
 
    // add edge 1-3
    graph->edge[3].src = 1;
    graph->edge[3].dest = 3;
 
    // add edge 2-3
    graph->edge[4].src = 2;
    graph->edge[4].dest = 3;
 
    // Use a different seed value for every run.
    srand(time(NULL));
 
    printf("\nCut found by Karger's randomized algo is %d\n",
           kargerMinCut(graph));
 
    return 0;
}


输出:

Contracting edge 0-2
Contracting edge 0-3

Cut found by Karger's randomized algo is 2