/// <summary> /// Konstruuje ścieżkę od źródła do danego wierzchołka /// </summary> /// <param name="s">Wierzchołek początkowy (źródło)</param> /// <param name="t">Wierzołek końcowy (cel)</param> /// <param name="pi">Tablica odległości od źródła</param> /// <exception cref="ArgumentException"></exception> /// <returns>Szukana ścieżka</returns> /// <remarks> /// Ścieżka reprezentowana jest jako tablica krawędzi, /// kolejne elementy tej tablicy to kolejne krawędzie na ścieżce.<para/> /// Jeśli ścieżka nie istnieje metoda zwraca null.<para/> /// Jeśli wierzchołek końcowy jest równy początkowemu metoda zwraca pustą (zeroelementową) tablicę. /// </remarks> /// <seealso cref="ConstructPath(int,int,PathsInfo[,])"/> /// <seealso cref="PathsInfo"/> /// <seealso cref="ASD.Graphs"/> public static Edge[] ConstructPath(int s, int t, PathsInfo[] pi) { if (pi[s].Dist != 0.0 || pi[s].Last != null) { throw new ArgumentException("Incorrect paths infos (invalid source vertex)"); } if (pi[t].Dist.IsNaN()) { return(null); } if (s == t) { return(new Edge[0]); } var edgesStack = new EdgesStack(); for (var vert = t; vert != s; vert = pi[vert].Last.Value.From) { edgesStack.Put(pi[vert].Last.Value); } return(edgesStack.ToArray()); }
/// <summary> /// Znajduje cykl o ujemnej długości (wadze) /// </summary> /// <param name="g">Badany graf</param> /// <param name="d">Informacje o najkrótszych ścieżkach</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 tablicy d powinny zawierać odległości wyznaczone algorytmem Forda-Bellmana, /// który zatrzymał się z wynikiem false.<para/> /// Jeśli analiza tablicy d nie wykryła cyklu o ujemnej długości metoda zwraca (0,null).<para/> /// Nie oznacza to, że w grafie nie ma żadnego cyklu o ujemnej dlugości, a jedynie że nie ma takiego cyklu, /// który zakłóciłby działanie uruchomionego wcześciej algorytmu Forda-Bellmana (dla danego źródła).<para/> /// Elementy (krawędzie) umieszczone są w zwracanej tablicy w kolejności swojego następstwa w znalezionym cyklu. /// </remarks> /// <seealso cref="ShortestPathsGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static (double weight, Edge[] cycle) FindNegativeCostCycle(this ASD.Graphs.Graph g, PathsInfo[] d) { Edge?edge = null; var vert = 0; while (edge == null && vert < g.VerticesCount) { if (!d[vert].Dist.IsNaN()) { foreach (var e in g.OutEdges(vert)) { if (!d[e.To].Dist.IsNaN() && !(d[e.To].Dist > d[vert].Dist + e.Weight)) { continue; } edge = e; break; } } vert++; } if (edge == null) { return(0.0, null); } var hashSet = new HashSet <int> { edge.Value.To }; var from = d[edge.Value.To].Last.Value.From; while (!hashSet.Contains(from)) { hashSet.Add(from); from = d[from].Last.Value.From; } var start = from; var weight = 0.0; var edgesStack = new EdgesStack(); do { edge = d[from].Last; edgesStack.Put(edge.Value); weight += edge.Value.Weight; from = edge.Value.From; }while (from != start); return(weight, edgesStack.ToArray()); }
/// <summary> /// Konstruuje ścieżkę pomiędzy wskazaną parą wierzchołków /// </summary> /// <param name="s">Wierzchołek początkowy (źródło)</param> /// <param name="t">Wierzołek końcowy (cel)</param> /// <param name="pi">Tablica odległości</param> /// <returns>Szukana ścieżka</returns> /// <remarks> /// Ścieżka reprezentowana jest jako tablica krawędzi, /// kolejne elementy tej tablicy to kolejne krawędzie na ścieżce.<para/> /// Jeśli ścieżka nie istnieje metoda zwraca null.<para/> /// Jeśli wierzchołek końcowy jest równy początkowemu metoda zwraca pustą (zeroelementową) tablicę. /// </remarks> /// <seealso cref="ConstructPath(int,int,PathsInfo[])"/> /// <seealso cref="PathsInfo"/> /// <seealso cref="ASD.Graphs"/> public static Edge[] ConstructPath(int s, int t, PathsInfo[,] pi) { if (pi[s, t].Dist.IsNaN()) { return(null); } if (s == t) { return(new Edge[0]); } var edgesStack = new EdgesStack(); for (var vert = t; vert != s; vert = pi[s, vert].Last.Value.From) { edgesStack.Put(pi[s, vert].Last.Value); } return(edgesStack.ToArray()); }
/// <summary> /// Bada czy graf nieskierowany jest acykliczny /// </summary> /// <param name="g">Badany graf</param> /// <returns>Informacja czy graf jest acykliczny</returns> /// <remarks> /// Metoda uruchomiona dla grafu skierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>. /// <br/> /// Graf wejściowy pozostaje niezmieniony. /// </remarks> public static bool IsUndirectedAcyclic(this Graph g) { if (g.Directed) { throw new System.ArgumentException("Graf jest skierowany"); } bool[] isVertexInCurrentPath = new bool[g.VerticesCount]; EdgesStack edgesInCurrentPath = new EdgesStack(); bool isAcyclic = g.GeneralSearchAll <EdgesStack>(v => { isVertexInCurrentPath[v] = true; return(true); }, v => { isVertexInCurrentPath[v] = false; return(true); }, e => { if (!edgesInCurrentPath.Empty && edgesInCurrentPath.Peek().From == e.To) { // Krawędź "powrotna" aktualnej ścieżki edgesInCurrentPath.Get(); return(true); } if (isVertexInCurrentPath[e.To]) { return(false); } edgesInCurrentPath.Put(e); return(true); }, out int cc); return(isAcyclic); }
/// <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> /// 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>Informacja czy ścieżka do wierzchołka docelowego istnieje</returns> /// <remarks> /// Domyślna wartość parametru h (null) oznacza, że zostanie przyjęte oszacowanie zerowe. /// Algorytm A* sprowadza się wówczas do algorytmu Dijkstry.<para/> /// 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ę).<para/> /// Informacja, czy szukana ścieżka istnieje, zawsze jest zwracana poprawnie. /// Jeśli nie istnieje (wynik false), to parametr p jest równy null. /// </remarks> /// <seealso cref="AStarGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static bool AStar(this Graph g, int s, int t, out Edge[] p, Func <int, int, double> h = null) { bool Cmp(KeyValuePair <int, double> keyValuePair1, KeyValuePair <int, double> keyValuePair2) { return(keyValuePair1.Value != keyValuePair2.Value ? keyValuePair1.Value < keyValuePair2.Value : keyValuePair1.Key < keyValuePair2.Key); } var priorityQueue = new PriorityQueue <int, double>(Cmp, CMonDoSomething.Nothing); var hashSet = new HashSet <int>(); var hashTable = new HashTable <int, double>(CMonDoSomething.Nothing); var hashTable2 = new HashTable <int, Edge>(CMonDoSomething.Nothing); if (h == null) { h = (i, j) => 0.0; } p = null; priorityQueue.Put(s, 0.0); hashTable[s] = 0.0; hashTable2[s] = new Edge(s, s, 0.0); var num = -1; while (!priorityQueue.Empty) { num = priorityQueue.Get(); hashSet.Add(num); if (num == t) { break; } foreach (var edge in g.OutEdges(num)) { if (hashSet.Contains(edge.To)) { continue; } if (!priorityQueue.Contains(edge.To)) { priorityQueue.Put(edge.To, double.PositiveInfinity); hashTable[edge.To] = double.PositiveInfinity; } if (!(hashTable[edge.To] > hashTable[num] + edge.Weight)) { continue; } hashTable[edge.To] = hashTable[num] + edge.Weight; hashTable2[edge.To] = edge; priorityQueue.ImprovePriority(edge.To, hashTable[edge.To] + h(edge.To, t)); } } if (num != t) { return(false); } var edgesStack = new EdgesStack(); for (var num3 = t; num3 != s; num3 = hashTable2[num3].From) { edgesStack.Put(hashTable2[num3]); } p = edgesStack.ToArray(); return(true); }
/// <summary> /// Wyznacza składowe dwuspójne /// </summary> /// <param name="g">Badany graf</param> /// <returns> /// Krotka (count, bcc, ap) składająca się z liczby składowych dwuspójnych, /// grafu opisującego te składowe i tablicy zawierającej punkty artykulacji /// </returns> /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu skierowanego</exception> /// <remarks> /// Wagi krawędzi w grafie opisującym składowe dwuspójne (bcc) odpowiadają numerom tych składowych.<para/> /// Metoda uruchomiona dla grafu skierowanego zgłasza wyjątek ArgumentException. /// </remarks> /// <seealso cref="BiconnectedGraphExtender"/> /// <seealso cref="ASD.Graphs"/> public static (int count, Graph bcc, int[] ap) BiconnectedComponents(this Graph g) { if (g.Directed) { throw new ArgumentException("Directed graphs are not allowed"); } var bcc = g.IsolatedVerticesGraph(); var edgesStack = new EdgesStack(); var discovery = new int[g.VerticesCount]; var low = new int[g.VerticesCount]; var visited = new bool[g.VerticesCount]; var isArticulation = new bool[g.VerticesCount]; var count = 0; var time = 0; int GetArticulationPoints(int i, int d) { var children = 0; visited[i] = true; low[i] = discovery[i] = time++; foreach (var edge in g.OutEdges(i)) { edgesStack.Put(edge); if (!visited[edge.To]) { var ap = GetArticulationPoints(edge.To, d); if (low[i] > ap) { low[i] = ap; } if ((i == d || discovery[i] > ap) && i != d) { continue; } if (i != d || ++children > 1) { isArticulation[i] = true; } Edge e; do { e = edgesStack.Get(); bcc.AddEdge(e.From, e.To, count); }while (e.From != i); count++; } else if (low[i] > discovery[edge.To]) { low[i] = discovery[edge.To]; } } return(low[i]); } for (var i = 0; i < g.VerticesCount; i++) { if (!visited[i]) { GetArticulationPoints(i, i); } } var list = new List <int>(); for (var j = 0; j < g.VerticesCount; j++) { if (isArticulation[j]) { list.Add(j); } } return(count, bcc, list.ToArray()); }
/// <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); }