private EdgesStack FindPath(Graph g, int from, int to) { EdgesStack edgesStack = new EdgesStack(); Predicate <Edge> onNewEdge = delegate(Edge e) { if (edgesStack.Empty || e.To != edgesStack.Peek().To) { edgesStack.Put(new Edge(e.To, e.From)); } return(true); }; Predicate <int> onLeaving = delegate(int n) { if (!edgesStack.Empty) { edgesStack.Get(); } return(true); }; Predicate <int> onEntering = delegate(int n) { return(n != to); }; g.GeneralSearchFrom <EdgesStack>(from, onEntering, onLeaving, onNewEdge); return(edgesStack); }
/// <summary> /// Podział grafu na cykle. Zakładamy, że dostajemy graf nieskierowany i wszystkie wierzchołki grafu mają parzyste stopnie /// (nie trzeba sprawdzać poprawności danych). /// </summary> /// <param name="G">Badany graf</param> /// <returns>Tablica cykli; krawędzie każdego cyklu powinny być uporządkowane zgodnie z kolejnością na cyklu, zaczynając od dowolnej</returns> /// <remarks> /// Metoda powinna działać w czasie O(m) /// </remarks> public static Edge[][] cyclePartition(this Graph G) { Graph gr = G.Clone(); Edge[][] eTable = new Edge[G.EdgesCount][]; EdgesStack eStack = new EdgesStack(); bool[] visited = new bool[gr.VerticesCount]; int numOfCycles = 0; DeepSearch(ref gr, ref eStack, ref visited, ref eTable, ref numOfCycles); int notNull = 0; foreach (var x in eTable) { if (x == null) { break; } else { notNull++; } } Edge[][] resultTable = new Edge[notNull][]; int i = 0; while (i < notNull) { resultTable[i] = eTable[i]; i++; } return(resultTable); }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Wymagamy, aby na ścieżce nie było powtórzeń wierzchołków ani krawędzi. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność to O(nD), gdzie D jest złożonością implementacji algorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość tej ścieżki</returns> public static double?FindSecondSimpleShortestPath(this Graph g, int a, int b, out Edge[] path) { path = null; PathsInfo[] bestPathInfo, tempPathInfo, secondBestPathInfo = null; double secondBest = double.PositiveInfinity; EdgesStack stack = new EdgesStack(); if (!g.DijkstraShortestPaths(a, out bestPathInfo)) { return(null); } int v = b; int pathCount = 0; while (bestPathInfo[v].Last != null) { stack.Put((Edge)bestPathInfo[v].Last); v = stack.Peek().From; pathCount++; } Edge deleted; while (!stack.Empty) { deleted = stack.Get(); g.DelEdge(deleted); g.DijkstraShortestPaths(a, out tempPathInfo); if (tempPathInfo[b].Dist < secondBest) { secondBestPathInfo = tempPathInfo; secondBest = tempPathInfo[b].Dist; } g.AddEdge(deleted); } List <Edge> pathList = new List <Edge>(); v = b; if (!double.IsPositiveInfinity(secondBest)) { while (secondBestPathInfo[v].Last != null) { pathList.Add((Edge)secondBestPathInfo[v].Last); v = pathList.Last().From; } pathList.Reverse(); path = pathList.ToArray(); } if (path == null) { return(null); } else { return(pathList.Sum(edge => edge.Weight)); } }
// Część 4 // Badanie czy graf nieskierowany jest acykliczny // 0.5 pkt // Parametry: // g - badany graf // Wynik: // true jeśli graf jest acykliczny, false jeśli graf nie jest acykliczny // Uwagi: // 1) Metoda uruchomiona dla grafu skierowanego powinna zgłaszać wyjątek Lab03Exception // 2) Graf wejściowy pozostaje niezmieniony // 3) Najpierw pomysleć jaki, prosty do sprawdzenia, warunek spełnia acykliczny graf nieskierowany // Zakodowanie tefo sprawdzenia nie powinno zająć więcej niż kilka linii! // Zadanie jest bardzo łatwe (jeśli wydaje się trudne - poszukać prostszego sposobu, a nie walczyć z trudnym!) public static bool Lab03IsUndirectedAcyclic(this Graph g) { if (g.Directed) { throw new Lab03Exception(); } EdgesStack edgesToVisit = new EdgesStack(); bool[] wasVisited = new bool[g.VerticesCount]; for (int i = 0; i < g.VerticesCount; i++) { if (wasVisited[i]) { continue; } wasVisited[i] = true; foreach (Edge e in g.OutEdges(i)) { edgesToVisit.Put(e); } while (!edgesToVisit.Empty) { Edge e = edgesToVisit.Get(); if (wasVisited[e.To]) { return(false); } wasVisited[e.To] = true; foreach (Edge e2 in g.OutEdges(e.To)) { if (e2.To == e.From) { continue; } edgesToVisit.Put(e2); } } } return(true); }
public static void DeepSearch(ref Graph G, ref EdgesStack eStack, ref bool[] visited, ref Edge[][] eTable, ref int numOfCycles) { Stack <int> vStack; int lastv = -1; for (int s = 0; s < G.VerticesCount; s++) { vStack = new Stack <int>(); while (G.OutDegree(s) > 0) { if (vStack.Count == 0) { vStack.Push(s); } int v = vStack.Peek(); if (G.OutDegree(v) == 0) { break; } if (visited[v]) { int i = 0; Edge temp; EdgesStack tempStack = new EdgesStack(); if (eStack.Count == 1) { temp = eStack.Get(); vStack.Pop(); G.DelEdge(temp); tempStack.Put(temp); i++; } else { do { temp = eStack.Get(); vStack.Pop(); G.DelEdge(temp); visited[temp.From] = false; tempStack.Put(temp); i++; } while (temp.From != v); if (eStack.Count != 0) { lastv = eStack.Peek().From; } } eTable[numOfCycles] = new Edge[i]; i = 0; while (!tempStack.Empty) { eTable[numOfCycles][i++] = tempStack.Get(); } numOfCycles++; continue; } else { visited[v] = true; foreach (var e in G.OutEdges(v)) { if (e.To != lastv) { vStack.Push(e.To); eStack.Put(e); break; } } lastv = v; } } } }
/// <summary> /// Wyznacza najkrótszą ścieżkę do wskazanego wierzchołka algorytmem A* /// </summary> /// <param name="g">Badany graf</param> /// <param name="s">Wierzchołek źródłowy</param> /// <param name="t">Wierzchołek docelowy</param> /// <param name="p">Znaleziona ścieżka (parametr wyjściowy)</param> /// <param name="h">Oszacowanie odległości wierzchołków (funkcja)</param> /// <returns>Długość ścieżki (jeśli nie istnieje to NaN)</returns> /// <remarks> /// Domyślna wartość parametru <i>h</i> (<b>null</b>) oznacza, że zostanie przyjęte oszacowanie zerowe. /// Algorytm A* sprowadza się wówczas do algorytmu Dijkstry.<br/> /// <br/> /// Metoda nie bada spełnienia założeń algorytmu A* - jeśli nie one są spełnione może zwrócić błędny wynik (nieoptymalną ścieżkę).<br/> /// Informacja, czy szukana ścieżka istnieje, zawsze jest zwracana poprawnie. /// Jeśli ścieżka nie istnieje (wynik <b>NaN</b>), to parametr <i>p</i> również jest równy <b>null</b>. /// </remarks> public static double AStar(this Graph g, int s, int t, out Edge[] p, System.Collections.Generic.Dictionary <int, string> description, System.Func <int, int, double> h = null) { var open = new PriorityQueue <int, double>((x, y) => x.Value < y.Value, Graph.Access); var close = new System.Collections.Generic.HashSet <int>(); // dodać referencję system.dll var dist = new HashTable <int, double>(Graph.Access); var last = new HashTable <int, Edge>(Graph.Access); if (h == null) { h = (x, y) => 0; } int n = g.VerticesCount; double[] approximateDistances = new double[n]; for (int i = 0; i < n; i++) { approximateDistances[i] = double.NaN; dist[i] = double.PositiveInfinity; } // Rozpoczęcie algorytmu przez dodanie wierzchołka startowego // (nie liczymy oszacowania odległości, bo to tylko niepotrzebna operacja w tym przypadku - // zawsze zaczniemy od startowego) approximateDistances[s] = 0; open.Put(s, 0); dist[s] = 0; while (!open.Empty) { int v = open.Get(); close.Add(v); if (v == t) { break; } foreach (Edge e in g.OutEdges(v)) { // Nie wracamy do już ustalonych wierzchołków if (close.Contains(e.To)) { continue; } // Sprawdzamy czy mamy lepszą odległość od źródła do danego wierzchołka double prospectiveDistance = dist[v] + e.Weight; if (dist[e.To] > prospectiveDistance) { dist[e.To] = prospectiveDistance; last[e.To] = e; if (approximateDistances[e.To].IsNaN()) { approximateDistances[e.To] = h(e.To, t); } double newPriority = approximateDistances[e.To] + dist[e.To]; if (open.Contains(e.To)) { open.ImprovePriority(e.To, newPriority); } else { open.Put(e.To, newPriority); } } } } foreach (int v in close) { description[v] = "close"; } while (!open.Empty) { description[open.Get()] = "open"; } if (double.IsPositiveInfinity(dist[t])) { p = null; return(double.NaN); } EdgesStack path = new EdgesStack(); int currentV = t; while (currentV != s) { Edge e = last[currentV]; path.Put(e); currentV = e.From; } p = path.ToArray(); return(dist[t]); }
/// <summary> /// Podział grafu na cykle. Zakładamy, że dostajemy graf nieskierowany i wszystkie wierzchołki grafu mają parzyste stopnie /// (nie trzeba sprawdzać poprawności danych). /// </summary> /// <param name="G">Badany graf</param> /// <returns>Tablica cykli; krawędzie każdego cyklu powinny być uporządkowane zgodnie z kolejnością na cyklu, zaczynając od dowolnej</returns> /// <remarks> /// Metoda powinna działać w czasie O(m) /// </remarks> public static Edge[][] cyclePartition(this Graph G) { List <Edge[]> cycles = new List <Edge[]>(); Graph h = G.Clone(); bool[] visited = new bool[h.VerticesCount]; EdgesStack edges = new EdgesStack(); int lastVertex = -1; while (h.EdgesCount > 0) { // Jesli zaczynamy szukac nowy cykl to: // - znajdz dowolny wierzcholek ktory ma jakies krawedzie // Wpp kontynuujemy z lastVertex int v = lastVertex; if (v == -1) { visited = new bool[h.VerticesCount]; edges = new EdgesStack(); for (int i = 0; i < h.VerticesCount; i++) { if (h.OutDegree(i) > 0) { v = i; break; } } } // v - wierzcholek, ktory ma co najmniej 1 krawedz // Wezmy pierwsza krawedz visited[v] = true; Edge edge; foreach (Edge e in h.OutEdges(v)) //TODO: WHY FOREACH? { // Usuwamy z grafu pierwsza krawedz i wrzucamy na stos h.DelEdge(e); edges.Put(e); // Napotkalismy cykl if (visited[e.To]) { Edge[] cycle = new Edge[edges.Count]; Edge firstEdge = edges.Get(); int i = 0; cycle[i++] = firstEdge; while (!edges.Empty) { Edge e2 = edges.Get(); cycle[i++] = e2; visited[e2.From] = visited[e2.To] = false; if (e2.From == firstEdge.To) { break; } } Array.Resize(ref cycle, i); Array.Reverse(cycle); cycles.Add(cycle); // Czy wierzcholek na ktorym skonczylismy ma jeszcze jakies krawedzie if (h.OutDegree(e.To) > 0) { lastVertex = e.To; } else { lastVertex = -1; } } else { lastVertex = e.To; } break; } } Edge[][] ret = new Edge[cycles.Count][]; int count = cycles.Count; for (int i = 0; i < count; i++) { ret[i] = cycles.Last(); cycles.RemoveAt(cycles.Count - 1); } return(ret); //TODO: find the efficient method //Graph g = G.Clone(); //Stack<Edge[]> cycles = new Stack<Edge[]>(); //Edge[] cycle; //while(FindCycle(g, out cycle)) //{ // cycles.Push(cycle); // foreach (var e in cycle) // { // g.DelEdge(e); // } //} //Edge[][] result = new Edge[cycles.Count][]; //int i = 0; //while(cycles.Count > 0) //{ // result[i++] = cycles.Pop(); //} //return result; }
public static bool FindCycle(this Graph g, out Edge[] cycle) { int[] visited = new int[g.VerticesCount]; // 0 - nieodwiedzony, 1 - szary, 2 - czarny int[] from = new int[g.VerticesCount]; EdgesStack edges = new EdgesStack(); cycle = new Edge[g.VerticesCount]; bool hasCycle = false; int cycleEnd = -1; int cc; Predicate <int> preVertex = delegate(int v) { visited[v] = 1; // szary return(true); }; Predicate <int> postVertex = delegate(int v) { visited[v] = 2; // czarny if (!edges.Empty) { edges.Get(); } return(true); }; Predicate <Edge> visitEdge = delegate(Edge e) { if (!g.Directed && from[e.From] == e.To) { return(true); } from[e.To] = e.From; edges.Put(e); if (visited[e.To] == 1) { hasCycle = true; cycleEnd = e.To; return(false); } return(true); }; g.GeneralSearchAll <EdgesStack>(preVertex, postVertex, visitEdge, out cc); if (hasCycle) { int i = 0; bool stop = false; while (!edges.Empty) { Edge e = edges.Get(); if (!stop) { cycle[i++] = e; } if (!edges.Empty && edges.Peek().To == cycleEnd) { stop = true; continue; } } Array.Resize <Edge>(ref cycle, i); Array.Reverse(cycle); return(true); } cycle = null; return(false); }