private static void CloseCycle(Graph g, ref Graph tree) { if (tree == null) { return; } int first = -1, last = -1; for (int i = 0; i < tree.VerticesCount; i++) { if (tree.InDegree(i) == 0) { first = i; } if (tree.OutDegree(i) == 0) { last = i; } } if (first == -1 || last == -1) { tree = null; return; } int?weight = g.GetEdgeWeight(last, first); if (weight.HasValue) { tree.AddEdge(last, first, weight.Value); } else { tree = null; } }
} // 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> /// Bada czy zadane grafy są jednakowe /// </summary> /// <param name="g">Pierwszy badany graf</param> /// <param name="h">Drugi badany graf</param> /// <returns>Informacja czy zadane grafy są jednakowe</returns> /// <remarks> /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.<para/> /// Metoda wykonuje obliczenia równolegle w wielu wątkach. /// </remarks> /// <seealso cref="GraphHelperExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool IsEqualParallel(this Graph g, Graph h) { if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount) { return(false); } if (g.Directed != h.Directed) { return(false); } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutDegree(i) != h.OutDegree(i) || g.InDegree(i) != h.InDegree(i)) { return(false); } } var result = Parallel.For(0, g.VerticesCount, (i, state) => { foreach (var edge in g.OutEdges(i)) { if (h.GetEdgeWeight(i, edge.To) != edge.Weight) { state.Stop(); } } }); return(result.IsCompleted); }
/// <summary> /// Bada czy zadane grafy są jednakowe /// </summary> /// <param name="g">Pierwszy badany graf</param> /// <param name="h">Drugi badany graf</param> /// <returns>Informacja czy zadane grafy są jednakowe</returns> /// <remarks>Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.</remarks> /// <seealso cref="GraphHelperExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool IsEqual(this Graph g, Graph h) { if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount) { return(false); } if (g.Directed != h.Directed) { return(false); } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutDegree(i) != h.OutDegree(i) || g.InDegree(i) != h.InDegree(i)) { return(false); } } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutEdges(i).Any(edge => h.GetEdgeWeight(i, edge.To) != edge.Weight)) { return(false); } } return(true); }
/// <summary> /// Bada czy zadane mapowanie wierzchołków definiuje izomorfizm grafów /// </summary> /// <param name="g">Pierwszy badany graf</param> /// <param name="h">Drugi badany graf</param> /// <param name="map">Zadane mapowanie wierzchołków</param> /// <returns></returns> /// <exception cref="ArgumentException"></exception> /// <remarks> /// Mapowanie wierzchołków zdefiniowane jest w ten sposób, /// że wierzchołkowi v w grafie g odpowiada wierzchołek map[v] w grafie h.<para/> /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.<para/> /// Metoda wykonuje obliczenia równolegle w wielu wątkach. /// </remarks> /// <seealso cref="IsomorphismGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool IsIsomorphicParallel(this Graph g, Graph h, int[] map) { if (g.VerticesCount != h.VerticesCount) { return(false); } if (g.EdgesCount != h.EdgesCount) { return(false); } if (g.Directed != h.Directed) { return(false); } if (map == null) { throw new ArgumentException("Invalid mapping"); } if (map.Length != g.VerticesCount) { throw new ArgumentException("Invalid mapping"); } var used = new bool[g.VerticesCount]; for (var i = 0; i < g.VerticesCount; i++) { if (map[i] < 0 || map[i] >= g.VerticesCount || used[map[i]]) { throw new ArgumentException("Invalid mapping"); } used[map[i]] = true; } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutDegree(i) != h.OutDegree(map[i]) || g.InDegree(i) != h.InDegree(map[i])) { return(false); } } var result = Parallel.For(0, g.VerticesCount, (i, state) => { foreach (var edge in g.OutEdges(i)) { if (edge.Weight != h.GetEdgeWeight(map[edge.From], map[edge.To])) { state.Stop(); } } }); return(result.IsCompleted); }
/// <summary> /// Bada czy zadane mapowanie wierzchołków definiuje izomorfizm grafów /// </summary> /// <param name="g">Pierwszy badany graf</param> /// <param name="h">Drugi badany graf</param> /// <param name="map">Zadane mapowanie wierzchołków</param> /// <returns></returns> /// <exception cref="ArgumentException"></exception> /// <remarks> /// Mapowanie wierzchołków zdefiniowane jest w ten sposób, /// że wierzchołkowi v w grafie g odpowiada wierzchołek map[v] w grafie h.<para/> /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia. /// </remarks> /// <seealso cref="IsomorphismGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool IsIsomorphic(this Graph g, Graph h, int[] map) { if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount) { return(false); } if (g.Directed != h.Directed) { return(false); } if (map == null) { throw new ArgumentException("Invalid mapping"); } if (map.Length != g.VerticesCount) { throw new ArgumentException("Invalid mapping"); } var used = new bool[g.VerticesCount]; for (var i = 0; i < g.VerticesCount; i++) { if (map[i] < 0 || map[i] >= g.VerticesCount || used[map[i]]) { throw new ArgumentException("Invalid mapping"); } used[map[i]] = true; } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutDegree(i) != h.OutDegree(map[i]) || g.InDegree(i) != h.InDegree(map[i])) { return(false); } } for (var i = 0; i < g.VerticesCount; i++) { if (g.OutEdges(i).Any(edge => edge.Weight != h.GetEdgeWeight(map[edge.From], map[edge.To]))) { return(false); } } return(true); }
// Helper method so as not to duplicate code in the CliqueNumberRecursive method private static void CheckNewCliqueVertex(Graph g, int v, int cliqueSize, ref bool[] isVertexInClique, ref bool[] biggestClique, ref int biggestCliqueSize) { // If vertex has a lower degree than there are already vertices in the clique // it definitely cannot be added to the clique if (g.OutDegree(v) < cliqueSize || g.InDegree(v) < cliqueSize) { return; } // Check if there are edges between this vertex (v) and all the vertices already in the clique bool validVertex = true; for (int cliqueVertex = 0; cliqueVertex < v; cliqueVertex++) { if (isVertexInClique[cliqueVertex] && (g.GetEdgeWeight(v, cliqueVertex).IsNaN() || g.GetEdgeWeight(cliqueVertex, v).IsNaN())) { validVertex = false; break; } } if (!validVertex) { return; } isVertexInClique[v] = true; bool[] newClique = isVertexInClique.Clone() as bool[]; int newCliqueSize = CliqueNumberRecursive(g, ref newClique, cliqueSize + 1, v + 1); if (newCliqueSize > biggestCliqueSize) { biggestClique = newClique; biggestCliqueSize = newCliqueSize; } isVertexInClique[v] = false; }
/// <summary> /// Bada izomorfizm grafów metodą pełnego przeglądu (rekurencyjnie) /// </summary> /// <param name="currentV">Aktualnie rozważany wierzchołek</param> /// <param name="map">Mapowanie wierzchołków grafu h na wierzchołki grafu g</param> /// <returns>Informacja czy znaleziono mapowanie definiujące izomotfizm</returns> internal bool FindMapping(int currentV, int[] map, bool[] isVertexInMapping) { // Wskazówki // 1) w sposób systematyczny sprawdzać wszystkie potencjalne mapowania // 2) unikać wielokrotnego sprawdzania tego samego mapowania // 3) zastosować algorytm z powrotami (backtracking) // 4) do badania krawędzi pomiędzy wierzchołkami i oraz j użyć metody GetEdgeWeight(i,j) // We assume that vertices 0 - (currentV - 1) are already mapped int n = g.VerticesCount; if (currentV >= n) { return(true); } // I assume that map[i] = j means, that vertex i in graph G is isomorphic with vertex j in graph H // We check every possible vertex from graph H that is not yet used // and try to match it with currentV (from graph G) for (int vH = 0; vH < n; vH++) { if (isVertexInMapping[vH]) { continue; } if (g.OutDegree(currentV) != h.OutDegree(vH) || g.InDegree(currentV) != h.InDegree(vH)) { continue; } // Check every neighbor of currentV that is already in mapping, if it matches neighbors of vH map[currentV] = vH; bool validMatch = true; foreach (Edge eG in g.OutEdges(currentV)) { // Edge leads to a neighbor that is not yet in the mapping (because we already analyzed vertices 0 - (currentVertex - 1)) if (eG.To > currentV) { continue; } // Edge weights should be equal in both graphs double edgeWeightH = h.GetEdgeWeight(vH, map[eG.To]); if (eG.Weight != edgeWeightH) { validMatch = false; break; } // The same goes for reverse edges double reverseEdgeWeightG = g.GetEdgeWeight(eG.To, currentV); double reverseEdgeWeightH = h.GetEdgeWeight(map[eG.To], vH); if ((!reverseEdgeWeightG.IsNaN() || !reverseEdgeWeightH.IsNaN()) && reverseEdgeWeightG != reverseEdgeWeightH) { validMatch = false; break; } } if (!validMatch) { continue; } // vH in H is isomorphic with currentV in G isVertexInMapping[vH] = true; bool isomorphismFound = FindMapping(currentV + 1, map, isVertexInMapping); if (isomorphismFound) { return(true); } isVertexInMapping[vH] = false; // Isomorphism with this matching not found, look for other possibilities of vertices isomorphic with currentV } // No isomorphism found return(false); }
/// <summary> /// Bada izomorfizm grafów metodą pełnego przeglądu (backtracking) /// </summary> /// <param name="g">Pierwszy badany graf</param> /// <param name="h">Drugi badany graf</param> /// <returns>Znalezione mapowanie wierzchołków</returns> /// <remarks> /// Jeśli grafy są izomorficzne metoda zwraca mapowanie wierzchołków grafu g na wierzchołki grafu h, /// w przeciwnym przypadku metoda zwraca null<para/> /// Odpowiedniość wierzchołków zdefiniowana jest w ten sposób, /// że wierzchołkowi v w grafie g odpowiada wierzchołek map[v] w grafie h.<para/> /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia. /// </remarks> /// <seealso cref="IsomorphismGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static int[] Isomorphism(this Graph g, Graph h) { if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount || g.Directed != h.Directed) { return(null); } var vertCount = g.VerticesCount; var mapping = new int[vertCount]; var mapped = new bool[vertCount]; var int_2 = new int[vertCount]; var int_3 = new int[vertCount]; for (var i = 0; i < vertCount; i++) { int_3[i] = -1; } var order = 0; var visited = new bool[vertCount]; bool PreVisitVertex(int i) { visited[i] = true; int_2[order++] = i; return(true); } bool VisitEdge(Edge edge) { if (!visited[edge.To] && int_3[edge.To] == -1) { int_3[edge.To] = edge.From; } return(true); } g.GeneralSearchAll <EdgesStack>(PreVisitVertex, null, VisitEdge, out _); bool FindIsomorphism(int vert) { (int int_0, int int_1)s; s.int_1 = vert; if (s.int_1 == vertCount) { return(true); } s.int_0 = int_2[s.int_1]; if (int_3[s.int_0] != -1) { return(h.OutEdges(mapping[int_3[s.int_0]]).Any(edge => Check(edge.To, ref s))); } for (var i = 0; i < vertCount; i++) { if (Check(i, ref s)) { return(true); } } return(false); } bool Check(int int_4, ref (int int_0, int int_1) pair) { if (mapped[int_4] || g.OutDegree(pair.int_0) != h.OutDegree(int_4) || g.InDegree(pair.int_0) != h.InDegree(int_4)) { return(false); } for (var i = 0; i < pair.int_1; i++) { var w1 = g.GetEdgeWeight(int_2[i], pair.int_0); var w2 = h.GetEdgeWeight(mapping[int_2[i]], int_4); if (w1 != w2 && (!w1.IsNaN() || !w2.IsNaN())) { return(false); } w1 = g.GetEdgeWeight(pair.int_0, int_2[i]); w2 = h.GetEdgeWeight(int_4, mapping[int_2[i]]); if (w1 != w2 && (!w1.IsNaN() || !w2.IsNaN())) { return(false); } } mapped[int_4] = true; mapping[pair.int_0] = int_4; if (FindIsomorphism(pair.int_1 + 1)) { return(true); } mapped[int_4] = false; return(false); } return(FindIsomorphism(0) ? mapping : null); }
/// <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
/// <summary> /// Znajduje scieżkę Eulera w grafie /// </summary> /// <param name="g">Badany graf</param> /// <param name="ec">Znaleziona ścieżka (parametr wyjściowy)</param> /// <returns>Informacja czy ścieżka Eulera istnieje</returns> /// <remarks> /// Jeśli w badanym grafie nie istnieje ścieżka Eulera metoda zwraca false, /// parametr ec ma wówczas wartość null.<para/> /// Metoda nie modyfikuje badanego grafu.<para/> /// Metoda implementuje algorytm Fleury'ego. /// </remarks> /// <seealso cref="EulerPathGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool EulerPath(this Graph g, out Edge[] ec) { ec = null; var oddDegreeCounter = 0; var startVertex = 0; var hasOutGreaterThanIn = false; var hasInGreaterThanOut = false; if (g.Directed) { for (var i = 0; i < g.VerticesCount; i++) { var outDegree = g.OutDegree(i); var inDegree = g.InDegree(i); if (Math.Abs(outDegree - inDegree) > 1) { return(false); } if (outDegree > inDegree) { if (hasOutGreaterThanIn) { return(false); } startVertex = i; hasOutGreaterThanIn = true; } if (inDegree > outDegree) { if (hasInGreaterThanOut) { return(false); } hasInGreaterThanOut = true; } } } else { for (var i = 0; i < g.VerticesCount; i++) { if ((g.OutDegree(i) & 1) != 1) { continue; } startVertex = i; if (++oddDegreeCounter > 2) { return(false); } } } var visited = new bool[g.VerticesCount]; var graph = g.Clone(); var s1 = new EdgesStack(); var s2 = new EdgesStack(); s2.Put(new Edge(startVertex, startVertex)); while (!s2.Empty) { var vertex = s2.Peek().To; visited[vertex] = true; if (graph.OutDegree(vertex) > 0) { var edge = graph.OutEdges(vertex).First(); s2.Put(edge); graph.DelEdge(edge); } else { s1.Put(s2.Get()); } } s1.Get(); if (graph.EdgesCount > 0) { return(false); } for (var i = 0; i < g.VerticesCount; i++) { if (!visited[i]) { return(false); } } ec = s1.ToArray(); return(true); }