} // TSP_Kruskal private static Graph GetTree(Graph g) { Graph helper = g.IsolatedVerticesGraph(true, g.VerticesCount); var queue = new EdgesMinPriorityQueue(); for (int i = 0; i < g.VerticesCount; i++) { foreach (var e in g.OutEdges(i)) { helper.AddEdge(e); queue.Put(e); } } Graph tree = g.IsolatedVerticesGraph(true, g.VerticesCount); UnionFind uf = new UnionFind(g.VerticesCount); while (tree.EdgesCount != tree.VerticesCount - 1) { if (queue.Empty) { return(null); } Edge e = queue.Get(); if (uf.Find(e.To) != uf.Find(e.From) && tree.OutDegree(e.From) < 1 && tree.InDegree(e.To) < 1) { tree.AddEdge(e); uf.Union(e.From, e.To); } } return(tree); }
/// <summary> /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Kruskala /// </summary> /// <param name="g">Badany graf</param> /// <returns> /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo /// </returns> /// <exception cref="ArgumentException"></exception> /// <remarks> /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/> /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/> /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli, to umożliwia jednolitą obsługę sytuacji /// gdy analizowany graf jest niespójny, wyznaczany jest wówczas las rozpinający. /// </remarks> /// <seealso cref="MSTGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static (double weight, Graph mst) Kruskal(this Graph g) { if (g.Directed) { throw new ArgumentException("Directed graphs are not allowed"); } var unionFind = new UnionFind(g.VerticesCount); var edgesMinPriorityQueue = new EdgesMinPriorityQueue(); var tree = g is AdjacencyMatrixGraph ? new AdjacencyListsGraph <AVLAdjacencyList>(false, g.VerticesCount) : g.IsolatedVerticesGraph(); var weight = 0.0; for (var i = 0; i < g.VerticesCount; i++) { foreach (var edge in g.OutEdges(i)) { if (edge.From < edge.To) { edgesMinPriorityQueue.Put(edge); } } } while (tree.EdgesCount < g.VerticesCount - 1 && !edgesMinPriorityQueue.Empty) { var edge = edgesMinPriorityQueue.Get(); if (!unionFind.Union(edge.From, edge.To)) { continue; } tree.AddEdge(edge); weight += edge.Weight; } return(weight, tree); }
/// <summary> /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Boruvki /// </summary> /// <param name="g">Badany graf</param> /// <returns> /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo /// </returns> /// <exception cref="ArgumentException"></exception> /// <remarks> /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/> /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/> /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli, /// to umożliwia jednolitą obsługę sytuacji gdy analizowany graf jest niespójny, /// wyznaczany jest wówczas las rozpinający.<para/> /// Jest to nieco zmodyfikowana wersja algorytmu Boruvki /// (nie ma operacji "sciągania" spójnych składowych w jeden wierzchołek). /// </remarks> /// <seealso cref="MSTGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static (double weight, Graph mst) Boruvka(this Graph g) { if (g.Directed) { throw new ArgumentException("Directed graphs are not allowed"); } var unionFind = new UnionFind(g.VerticesCount); var edgesMinPriorityQueue = new EdgesMinPriorityQueue(); var tree = g is AdjacencyMatrixGraph ? new AdjacencyListsGraph <AVLAdjacencyList>(false, g.VerticesCount) : g.IsolatedVerticesGraph(); var weight = 0.0; var change = true; while (change) { change = false; for (var i = 0; i < g.VerticesCount; i++) { Edge?edge = null; foreach (var e in g.OutEdges(i)) { if (unionFind.Find(i) != unionFind.Find(e.To) && (edge == null || e.Weight < edge.Value.Weight)) { edge = e; } } if (edge != null) { edgesMinPriorityQueue.Put(edge.Value); } } while (!edgesMinPriorityQueue.Empty) { var edge = edgesMinPriorityQueue.Get(); if (!unionFind.Union(edge.From, edge.To)) { continue; } tree.AddEdge(edge); weight += edge.Weight; if (tree.EdgesCount == g.VerticesCount - 1) { return(weight, tree); } change = true; } } return(weight, tree); }
public static Graph Kruskal(Graph graph) { var edges = getGraphEdgesMin(graph); UnionFind uf = new UnionFind(graph.VerticesCount); var retGraph = new AdjacencyMatrixGraph(false, graph.VerticesCount); foreach (Edge e in edges.ToArray()) { if (uf.Find(e.From) != uf.Find(e.To)) { retGraph.AddEdge(e); uf.Union(e.From, e.To); } } return(retGraph); }
/// <summary> /// Znajduje rozwiązanie przybliżone problemu komiwojażera algorytmem zachłannym "kruskalopodobnym" /// </summary> /// <param name="g">Badany graf</param> /// <returns>Krotka (weight, cycle) składająca się z długości (sumy wag krawędzi) znalezionego cyklu i tablicy krawędzi tworzących ten cykl)</returns> /// <remarks> /// Elementy (krawędzie) umieszczone są w tablicy cycle w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<para/> /// Jeśli algorytm "kruskalopodobny" nie znajdzie w badanym grafie cyklu Hamiltona (co oczywiście nie znaczy, że taki cykl nie istnieje) to metoda zwraca krotkę (NaN,null).<para/> /// Metodę można stosować dla grafów skierowanych i nieskierowanych.<para/> /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi. /// </remarks> /// <seealso cref="AproxTSPGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static (double weight, Edge[] cycle) KruskalTSP(this Graph g) { if (g.VerticesCount <= (g.Directed ? 1 : 2)) { return(double.NaN, null); } var graph = !(g is AdjacencyMatrixGraph) ? g.IsolatedVerticesGraph() : new AdjacencyListsGraph <SimpleAdjacencyList>(g.Directed, g.VerticesCount); var unionFind = new UnionFind(g.VerticesCount); var edgesMinPriorityQueue = new EdgesMinPriorityQueue(); for (var i = 0; i < g.VerticesCount; i++) { foreach (var edge in g.OutEdges(i)) { if (g.Directed || edge.From < edge.To) { edgesMinPriorityQueue.Put(edge); } } } while (graph.EdgesCount < g.VerticesCount && !edgesMinPriorityQueue.Empty) { var edge = edgesMinPriorityQueue.Get(); if (graph.OutDegree(edge.From) >= (g.Directed ? 1 : 2) || graph.InDegree(edge.To) >= (g.Directed ? 1 : 2)) { continue; } if (unionFind.Union(edge.From, edge.To) || graph.EdgesCount == g.VerticesCount - 1) { graph.AddEdge(edge); } } if (graph.EdgesCount < g.VerticesCount) { return(double.NaN, null); } var cycle = new Edge[g.VerticesCount]; var weight = 0.0; var prevVert = -1; var vert = 0; for (var i = 0; i < g.VerticesCount;) { foreach (var edge in graph.OutEdges(vert)) { if (edge.To == prevVert) { continue; } cycle[i++] = edge; weight += edge.Weight; prevVert = vert; vert = edge.To; break; } } return(weight, cycle); }
/// <summary> /// Znajduje rozwiązanie przybliżone problemu komiwojażera algorytmem zachłannym "kruskalopodobnym" /// </summary> /// <param name="g">Badany graf</param> /// <param name="cycle">Znaleziony cykl (parametr wyjściowy)</param> /// <returns>Długość znalezionego cyklu (suma wag krawędzi)</returns> /// <remarks> /// Elementy (krawędzie) umieszczone są w tablicy <i>cycle</i> w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<br/> /// <br/> /// Jeśli algorytm "kruskalopodobny" nie znajdzie w badanym grafie cyklu Hamiltona /// (co oczywiście nie znaczy, że taki cykl nie istnieje) to metoda zwraca <b>null</b>, /// parametr wyjściowy <i>cycle</i> również ma wówczas wartość <b>null</b>.<br/> /// <br/> /// Metodę można stosować dla grafów skierowanych i nieskierowanych.<br/> /// <br/> /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi. /// </remarks> public static double TSP_Kruskal(this Graph g, out Edge[] cycle) { // ToDo - algorytm "kruskalopodobny" int n = g.VerticesCount; EdgesMinPriorityQueue edgesQueue = new EdgesMinPriorityQueue(); for (int v = 0; v < n; v++) { foreach (Edge e in g.OutEdges(v)) { // For undirected graphs only add edges once if (!g.Directed && e.From >= e.To) { continue; } edgesQueue.Put(e); } } UnionFind uf = new UnionFind(n); Graph minSpanningTree = g.IsolatedVerticesGraph(); while (!edgesQueue.Empty && minSpanningTree.EdgesCount < n - 1) { Edge e = edgesQueue.Get(); if (uf.Find(e.From) == uf.Find(e.To)) // Edge would preemptively create a cycle { continue; } if (g.Directed) { if (minSpanningTree.OutDegree(e.From) != 0 || minSpanningTree.InDegree(e.To) != 0) // Two out edges or two in edges for some vertex { continue; } } else { if (minSpanningTree.OutDegree(e.From) == 2 || minSpanningTree.OutDegree(e.To) == 2) // Edge would create a diversion on the path { continue; } } minSpanningTree.AddEdge(e); uf.Union(e.From, e.To); } if (minSpanningTree.EdgesCount < n - 1) { // Unable to construct a spanning path with n-1 edges cycle = null; return(double.NaN); } // Look for vertices at the beginning and end of the path int cycleBeginV = -1, cycleEndV = -1; for (int v = 0; v < n; v++) { if (!minSpanningTree.Directed) { if (minSpanningTree.OutDegree(v) == 1) { if (cycleBeginV == -1) { cycleBeginV = v; } else { cycleEndV = v; break; } } } else { if (minSpanningTree.OutDegree(v) == 0) { cycleBeginV = v; } if (minSpanningTree.InDegree(v) == 0) { cycleEndV = v; } if (cycleBeginV != -1 && cycleEndV != -1) { break; } } } if (cycleBeginV == -1 || cycleEndV == -1) { // This if is superfluous, but I'm leaving it just for clarity cycle = null; return(double.NaN); } // Closing the cycle minSpanningTree.AddEdge(new Edge(cycleBeginV, cycleEndV, g.GetEdgeWeight(cycleBeginV, cycleEndV))); cycle = new Edge[n]; int currentCycleV = 0; double cycleLength = 0; for (int i = 0; i < n; i++) { Edge?cycleEdge = minSpanningTree.OutEdges(currentCycleV).First(); if (!minSpanningTree.Directed && i > 0) { // Make sure the edge goes further into the cycle, not backwards (only for undirected graphs) foreach (Edge e in minSpanningTree.OutEdges(currentCycleV)) { if (e.To != cycle[i - 1].From) { cycleEdge = e; break; } } } cycle[i] = cycleEdge.Value; currentCycleV = cycleEdge.Value.To; cycleLength += cycleEdge.Value.Weight; } return(cycleLength); } // TSP_Kruskal
} // TSP_Kruskal /// <summary> /// Znajduje rozwiązanie przybliżone problemu komiwojażera tworząc cykl Hamiltona na podstawie drzewa rozpinającego /// </summary> /// <param name="g">Badany graf</param> /// <param name="cycle">Znaleziony cykl (parametr wyjściowy)</param> /// <returns>Długość znalezionego cyklu (suma wag krawędzi)</returns> /// <remarks> /// Elementy (krawędzie) umieszczone są w tablicy <i>cycle</i> w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<br/> /// <br/> /// Jeśli algorytm bazujący na drzewie rozpinającym nie znajdzie w badanym grafie cyklu Hamiltona /// (co oczywiście nie znaczy, że taki cykl nie istnieje) to metoda zwraca <b>null</b>, /// parametr wyjściowy <i>cycle</i> również ma wówczas wartość <b>null</b>.<br/> /// <br/> /// Metodę można stosować dla grafów nieskierowanych.<br/> /// Zastosowana do grafu skierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.<br/> /// <br/> /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi.<br/> /// <br/> /// Dla grafu nieskierowanego spełniajacego nierówność trójkąta metoda realizuje algorytm 2-aproksymacyjny. /// </remarks> public static double TSP_TreeBased(this Graph g, out Edge[] cycle) { if (g.Directed) { throw new System.ArgumentException("Graph is directed"); } int n = g.VerticesCount; EdgesMinPriorityQueue edgesQueue = new EdgesMinPriorityQueue(); for (int v = 0; v < n; v++) { foreach (Edge e in g.OutEdges(v)) { // Only add edges once if (e.From >= e.To) { continue; } edgesQueue.Put(e); } } Graph minSpanningTree = g.IsolatedVerticesGraph(); UnionFind uf = new UnionFind(n); while (!edgesQueue.Empty && minSpanningTree.EdgesCount < n - 1) { Edge e = edgesQueue.Get(); if (uf.Find(e.From) == uf.Find(e.To)) // Same component, would create a cycle { continue; } minSpanningTree.AddEdge(e); } if (minSpanningTree.EdgesCount < n - 1) { } int[] preorderVertices = new int[n]; int i = 0; minSpanningTree.GeneralSearchAll <EdgesQueue>(v => { preorderVertices[i++] = v; return(true); }, null, null, out int cc); cycle = new Edge[n]; double cycleLength = 0; for (i = 1; i < n; i++) { int v1 = preorderVertices[i - 1], v2 = preorderVertices[i]; Edge e = new Edge(v1, v2, g.GetEdgeWeight(v1, v2)); cycle[i - 1] = e; cycleLength += e.Weight; if (e.Weight.IsNaN()) { break; // Added an edge that doesn't exist } } if (cycleLength.IsNaN()) { cycle = null; return(double.NaN); } double closingEdgeWeight = g.GetEdgeWeight(preorderVertices[n - 1], preorderVertices[0]); cycle[n - 1] = new Edge(preorderVertices[n - 1], preorderVertices[0], closingEdgeWeight); cycleLength += closingEdgeWeight; return(cycleLength); } // TSP_TreeBased