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; } }
/// <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); }
} // 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); }
public static bool BFSColoring(Graph g, int v, ref COLORS [] col) { Queue <int> q = new Queue <int>(); int vert; int partition_class = 0; int i; q.Enqueue(v); while (q.Count != 0) { i = 0; vert = q.Dequeue(); if (col[vert] == COLORS.NONE || col[vert] == ((partition_class % 2 == 0) ? COLORS.RED : COLORS.BLUE)) { if (col[vert] == COLORS.NONE) { col[vert] = (partition_class++ % 2 == 0) ? COLORS.RED : COLORS.BLUE; } foreach (Edge e in g.OutEdges(vert)) { if (col[e.To] == COLORS.NONE) { if (col[vert] == COLORS.RED) { col[e.To] = COLORS.BLUE; } else { col[e.To] = COLORS.RED; } q.Enqueue(e.To); } else if (col[e.To] == col[vert]) { Console.WriteLine("Vertex {0} (Color {1}) is connected with Vertex {2} (Color {3})", e.To, col[e.To], vert, col[vert]); return(false); } else { i++; } } if (i == g.OutDegree(v)) { partition_class--; } } foreach (Edge e in g.OutEdges(vert)) { if (col[e.To] == col[vert]) { return(false); } } partition_class++; } 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; }
public static List <int> ConstructHamiltonianPath(Graph spanning) { prohibitedVertices = new List <int>(); spanningTree = spanning; int n_vertices = spanningTree.VerticesCount; // znajdz pierwszy mozliwy wierzcholek ze stopniem wiekszym niz 2 // Jesli nie ma takiego wierzcholka (drzewo jest sciezka bez rozgalezien) - wez pierwszy wierzcholek int notLeafNum = 0; for (int i = 0; i < n_vertices; i++) { if (spanningTree.OutDegree(i) > 2) { notLeafNum = i; break; } } // Ekslorujemy wokól tego wierzcholka. Ten wierzcholek to nasze v. var res = Merge(notLeafNum, null, null).hamiltionianPath; return(res); }
/// <summary> /// Przeszukuje graf począwszy od wskazanego wierzchołka /// </summary> /// <param name="g">Badany graf</param> /// <param name="from">Wierzchołek startowy</param> /// <param name="preVisitVertex">Metoda wywoływana przy pierwszym wejściu do wierzchołka</param> /// <param name="postVisitVertex">Metoda wywoływana przy ostatecznym opuszczaniu wierzchołka</param> /// <param name="visitEdge">Metoda wywoływana dla odwiedzanych krawędzi</param> /// <param name="visitedVertices">Tablica odwiedzonych wierzchołków</param> /// <typeparam name="TEdgesContainer">Typ kolekcji przechowującej krawędzie</typeparam> /// <returns>Informacja czy zbadano wszystkie krawędzie osiągalne z wierzchołka startowego</returns> /// <exception cref="ArgumentException"></exception> /// <remarks> /// Metoda odwiedza jedynie te wierzchołki, dla których elementy tablicy visitedVertices /// mają wartość false (i zmienia wpisy w visitedVertices na true).<para/> /// Tablica visitedVertices musi mieć rozmiar zgodny z liczbą wierzchołków grafu g.<para/> /// Domyślna wartość null parametru visitedVertices oznacza tablicę wypełnioną wartościami /// false (zostanie ona utworzona wewnątrz metody).<para/> /// Parametrem metod preVisitVertex i postVisitVertex jest numer odwiedzanego wierzchołka.<para/> /// Metoda preVisitVertex jest wywoływana gdy przeszukiwanie grafu osiąga dany wierzchołek /// (jest on już ozmaczony jako odwiedzony, ale wychodzące z niego krawędzie /// nie są jeszcze dodane do kolekcji.<para/> /// Metoda postVisitVertex jest wywoływana gdy z kolekcji została usunięta /// (i w pełni przetworzona) ostatnia krawędź wychodząca z danego wierzchołka.<para/> /// Korzystanie z metody postVisitVertex jest dozwolone jedynie /// gdy kontenerem przechowującym krawędzie jest <see cref="EdgesStack"/> czyli dla /// przeszukiwania w głąb (DFS) z jawnym stosem (w innych przypadkach /// metoda zgłasza wyjątek <see cref="ArgumentException"/>).<para/> /// Metoda visitEdge jest wywoływana dla każdej krawędzi rozważanej /// podczas przeszukiwania (nawet jeśli nie została wykorzystana), /// krawędź ta jest parametrem metody visitEdge.<para/> /// Wyniki zwracane przez metody preVisitVertex, postVisitVertex /// i visitEdge oznaczają żądanie kontynuowania (true) lub przerwania (false) obliczeń.<para/> /// Parametry preVisitVertex, postVisitVertex i visitEdge mogą mieć wartość null, /// co oznacza metodę pustą (nie wykonującą żadnych działań, zwracającą true).<para/> /// Jeśli zostały zbadane wszystkie krawędzie osiągalne z wierzchołka startowego metoda zwraca true, /// jeśli obliczenia zostały przerwane przez metodę związaną z parametrem preVisitVertex, /// postVisitVertex lub visitEdge metoda zwraca false.<para/> /// Wynik true nie oznacza, że metoda odwiedziła wszystkie krawędzie grafu /// (oznacza jedynie, że odwiedziła wszystkie krawędzie osiągalne z wierzchołka startowego). /// </remarks> /// <seealso cref="GeneralSearchGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool GeneralSearchFrom <TEdgesContainer>(this Graph g, int from, Predicate <int> preVisitVertex, Predicate <int> postVisitVertex, Predicate <Edge> visitEdge, bool[] visitedVertices = null) where TEdgesContainer : IEdgesContainer, new() { if (postVisitVertex != null && typeof(TEdgesContainer) != typeof(EdgesStack)) { throw new ArgumentException("Parameter postVisitVertex must be null for containers other than EdgesStack"); } var stack = new Stack <int>(); var edgesContainer = Activator.CreateInstance <TEdgesContainer>(); if (visitedVertices == null) { visitedVertices = new bool[g.VerticesCount]; } else if (visitedVertices.Length != g.VerticesCount) { throw new ArgumentException("Invalid visitedVertices length"); } if (visitedVertices[from]) { throw new ArgumentException("Start vertex is already visited"); } if (visitEdge == null) { visitEdge = edge => true; } if (preVisitVertex == null) { preVisitVertex = i => true; } visitedVertices[from] = true; stack.Push(from); if (!preVisitVertex(from)) { return(false); } if (postVisitVertex != null && g.OutDegree(from) == 0) { return(postVisitVertex(from)); } foreach (var edge in g.OutEdges(from)) { edgesContainer.Put(edge); } while (true) { if (edgesContainer.Empty) { return(true); } var edge = edgesContainer.Get(); if (!visitEdge(edge)) { return(false); } if (!visitedVertices[edge.To]) { visitedVertices[edge.To] = true; stack.Push(edge.To); if (!preVisitVertex(edge.To)) { break; } foreach (var e in g.OutEdges(edge.To)) { edgesContainer.Put(e); } } if (postVisitVertex == null) { continue; } while (edgesContainer.Empty || stack.Peek() != edgesContainer.Peek().From) { if (!postVisitVertex(stack.Pop())) { return(false); } if (stack.Count == 0) { break; } } } return(false); }
/// <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 <b>false</b>, parametr <i>ec</i> ma wówczas wartość <b>null</b>.<br/> /// <br/> /// Metoda nie modyfikuje badanego grafu.<br/> /// <br/> /// Metoda implementuje algorytm Fleury'ego. /// </remarks> public static bool Lab04_Euler(this Graph g, out Edge[] ec) { // tylko cykl - 2 pkt // cykl i sciezka - 3 pkt /* * Algorytm Fleury'ego * * utworz pusty stos krawedzi Euler * utworz pusty stos krawedzi pom * w = dowolny wierzcholek grafu * umiesc na stosie pom sztuczna krawedz <w,w> * dopoki pom jest niepusty powtarzaj * w = wierzch. koncowy krawedzi ze szczytu stosu pom (bez pobierania krawedzi ze stosu) * jesli stopien wychodzacy w > 0 * e = dowolna krawedz wychodzaca z w * umiesc krawedz e na stosie pom * usun krawedz e z grafu * w przeciwnym przypadku * pobiez szczytowy element ze stosu pom i umiesc go na stosie Euler * usun ze stosu Euler sztuczna krawedz (petle) startowa (jest na szczycie) * * wynik: krawedzie tworzace cykl sa na stosie Euler * * Uwaga: powyzszy algorytm znajduje cykl Eulera (jesli istnieje), * aby znalezc sciezke nalezy najpierw wyznaczyc wierzcholek startowy * (nie mozna wystartowac z dowolnego) */ EdgesStack Euler = new EdgesStack(); EdgesStack pom = new EdgesStack(); int w = 0; pom.Put(new Edge(w, w)); Graph clonedGraph = g.Clone(); // Trzeba byłoby sprawdzić jeszcze czy g jest Eulerowski (czy wszystkie wierzchołki są // stopnia parzystego) while (!pom.Empty) { w = pom.Peek().To; if (clonedGraph.OutDegree(w) > 0) { Edge e = clonedGraph.OutEdges(w).First(); pom.Put(e); clonedGraph.DelEdge(e); } else { Euler.Put(pom.Get()); } } Euler.Get(); ec = Euler.ToArray(); if (ec.First().From == ec.Last().To) { return(true); } else { ec = null; return(false); } }
/// <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 <b>false</b>, parametr <i>ec</i> ma wówczas wartość <b>null</b>.<br/> /// <br/> /// Metoda nie modyfikuje badanego grafu.<br/> /// <br/> /// Metoda implementuje algorytm Fleury'ego. /// </remarks> public static bool Lab04_Euler(this Graph g, out Edge[] ec) { // tylko cykl - 2 pkt // cykl i sciezka - 3 pkt //check if graph has euler cycle or path int numOfOddVertices = 0, cc = 0; int oddVertex = -1; Predicate <int> preVertex = delegate(int vertex) { if (g.OutDegree(vertex) % 2 != 0) { numOfOddVertices++; oddVertex = vertex; if (numOfOddVertices > 2) { return(false); } } return(true); }; GeneralSearchGraphExtender.GeneralSearchAll <EdgesStack>(g, preVertex, null, null, out cc); if (cc != 1 || numOfOddVertices > 2) { ec = null; return(false); } Graph h = g.Clone(); //find euler cycle //if(0 == numOfOddVertices) //{ EdgesStack euler = new EdgesStack(); EdgesStack tmp = new EdgesStack(); int v = -1 == oddVertex ? 0 : oddVertex; //h.OutDegree(0) > 0, if start with oddVertex algorithm finds euler path tmp.Put(new Edge(v, v)); //dummy edge to remove later while (!tmp.Empty) { v = tmp.Peek().To; if (h.OutDegree(v) > 0) { foreach (var e in h.OutEdges(v)) { tmp.Put(e); h.DelEdge(e); break; } } else { euler.Put(tmp.Get()); } } euler.Get(); //dummy edge ec = new Edge[euler.Count]; for (int i = 0; i < ec.Length; i++) { ec[i] = euler.Get(); } return(true); //} /* * Algorytm Fleury'ego * * utworz pusty stos krawedzi Euler * utworz pusty stos krawedzi pom * w = dowolny wierzcholek grafu * umiesc na stosie pom sztuczna krawedz <w,w> * dopoki pom jest niepusty powtarzaj * w = wierzch. koncowy krawedzi ze szczytu stosu pom (bez pobierania krawedzi ze stosu) * jesli stopien wychodzacy w > 0 * e = dowolna krawedz wychodzaca z w * umiesc krawedz e na stosie pom * usun krawedz e z grafu * w przeciwnym przypadku * pobiez szczytowy element ze stosu pom i umiesc go na stosie Euler * usun ze stosu Euler sztuczna krawedz (petle) startowa (jest na szczycie) * * wynik: krawedzie tworzace cykl sa na stosie Euler * * Uwaga: powyzszy algorytm znajduje cykl Eulera (jesli istnieje), * aby znalezc sciezke nalezy najpierw wyznaczyc wierzcholek startowy * (nie mozna wystartowac z dowolnego) */ }
/// <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); }
/// <summary> /// i - obecny wierzcholek, v /// j - obecny kierunek, w którym idziemy /// prohibited - wierzcholek z którego wlasnie przyszlismy, wiec nie mozemy juz tam pójsc /// </summary> static MergeRet Merge(int i, int?j, int?prohibited) { // ten przypadek dzieje sie, gdy jestesmy na jakichs rozgalezieniach if (j == null) { // Kierunki, w które mozemy pójsc z aktualnego wierzcholka var dirs = spanningTree.OutEdges(i).Select(x => x.To); // Trzeba zabronic chodzenia w kierunki, gdzie juz bylismy (od których zaczynalismy). //Do tego sluzy argument "prohibited" List <MergeRet> toBind = new List <MergeRet>(); foreach (var dir in dirs) { if (!(prohibited.HasValue && dir == prohibited.Value)) { toBind.Add(Merge(i, dir, null)); } } List <int> hampath = Bind(toBind, i); return(new MergeRet { hamiltionianPath = hampath, v_i = i, x_i = hampath[1] }); } else { // j nie jest nullem - jestesmy w czesci grafu, gdzie idziemy w jakims kierunku // sprawdzamy, czy ta część grafu jest ścieżką (e.g. i-j-k-l). // Jeśli tak, zwracany ścieżkę Hamiltona od i do j // Jeśli nie, idziemy rekurencujnie od i-tego wierzchołka (ten i-ty wierzchołek będzie naszym v). // in this case we will need to prevent returning to previously seen vertices var verticesList = new List <int>(); int curr = j.Value; int prev = i; verticesList.Add(curr); int branchLength = 1; while (true) { if (spanningTree.OutDegree(curr) == 1) { break; } else if (spanningTree.OutDegree(curr) > 2) { var forward = new List <int>(); var backward = new List <int>(); if (verticesList.Count() == 1) { if (!prohibitedVertices.Contains(i)) { forward.Add(i); prohibitedVertices.Add(i); } } else { if (verticesList.Count % 2 == 0) { for (int k = 1; k < verticesList.Count - 2; k += 2) { forward.Add(verticesList[k]); } forward.Add(verticesList[verticesList.Count - 2]); for (int k = verticesList.Count - 4; k > -1; k -= 2) { backward.Add(verticesList[k]); } } else { for (int k = 1; k < verticesList.Count; k += 2) { forward.Add(verticesList[k]); } for (int k = verticesList.Count - 3; k > -1; k -= 2) { backward.Add(verticesList[k]); } } } // return forward U merge U back var merge = Merge(curr, null, prev); merge.hamiltionianPath = forward.Concat(merge.hamiltionianPath).Concat(backward).ToList(); return(merge); } int tmp = spanningTree.OutEdges(curr).Where(x => x.To != prev).ToArray()[0].To; prev = curr; curr = tmp; branchLength++; verticesList.Add(curr); } // spanningTree.OutDegree(curr) == 1 // laczymy v_i z x_i // Mozliwe przypadki: // 1. Jest jedynie v_i - w tym etapie nic nie laczymy, bo za wczesnie // 2. Jest v_i i x_i - w tym etapie nic nie laczymy, bo za wczesnie // 3. Oprócz v_i i x_i sa jeszcze inne wierzcholki - laczymy if (branchLength == 1) { return(new MergeRet { v_i = curr, x_i = null, hamiltionianPath = null }); } else if (branchLength == 2) { return(new MergeRet { v_i = prev, x_i = curr, hamiltionianPath = null }); } else { MergeRet ret = new MergeRet(); List <int> hamCycle = new List <int>(); ret.v_i = verticesList[0]; ret.x_i = verticesList[1]; int numVert = verticesList.Count(); //forward for (int k = 1; k < numVert; k += 2) { hamCycle.Add(verticesList[k]); } //backward for (int k = ((numVert - 1) / 2) * 2; k > -1; k -= 2) { hamCycle.Add(verticesList[k]); } ret.hamiltionianPath = hamCycle; return(ret); } } }