예제 #1
0
        /// <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());
        }
예제 #3
0
        /// <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());
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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);
            }
        }
예제 #6
0
        /// <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)
             */
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }