Beispiel #1
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);
            }
        }
        /// <summary>
        /// Wyznacza maksymalny przepływ o minimalnym koszcie
        /// </summary>
        /// <param name="g">Graf przepustowości</param>
        /// <param name="c">Graf kosztów</param>
        /// <param name="source">Wierzchołek źródłowy</param>
        /// <param name="target">Wierzchołek docelowy</param>
        /// <param name="parallel">Informacja czy algorytm ma korzystać z równoległej wersji algorytmu Forda-Bellmana</param>
        /// <param name="mf">Metoda wyznaczania wstępnego maksymalnego przepływu (bez uwzględniania kosztów)</param>
        /// <param name="af">Metoda powiększania przepływu</param>
        /// <param name="matrixToAVL">Czy optymalizować sposób reprezentacji grafu rezydualnego</param>
        /// <returns>
        /// Krotka (value, cost, flow) składająca się z
        /// wartości maksymalnego przepływu, jego kosztu i grafu opisującego ten przepływ
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Można wybrać metodę wyznaczania wstępnego maksymalnego przepływu (parametr mf).<para/>
        /// Domyślna wartość mf (null) oznacza konstrukcję wstępnego przepływu z wykorzystaniem sztucznej krawędzi.<para/>
        /// Można wybrać metodę powiększania przepływu (parametr pf).<para/>
        /// Znaczenie tego parametru zależy od wybranej metody wyznaczania wstępnego maksymalnego przepływu
        /// (parametr mf), dla niektórych wartości parametru mf
        /// (np. <see cref="MaxFlowGraphExtender.FordFulkersonDinicMaxFlow"/>)
        /// jawne wskazanie metody powiększania przepływu jest konieczne
        /// (pozostawienie domyślnej wartości parametru pf (null)
        /// spowoduje zgłoszenie wyjątku <see cref="ArgumentException"/>).<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego lub grafu
        /// z ujemnymi przepustowościami krawędzi zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy grafy przepustowości i kosztów mają różną strukturę lub parametry
        /// source i target są równe metoda również zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy w grafie przepustowości istnieją krawędzie w obu kierunkach
        /// pomiędzy parą wierzchołków metoda również zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="MinCostFlowGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double value, double cost, Graph flow) MinCostFlow(this Graph g, Graph c, int source, int target, bool parallel = false, MaxFlow mf = null, AugmentFlow af = null, bool matrixToAVL = true)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }
            if (source == target)
            {
                throw new ArgumentException("Source and target must be different");
            }
            if (g.VerticesCount != c.VerticesCount)
            {
                throw new ArgumentException("Inconsistent capacity and cost graphs");
            }

            var fordBellmanShortestPaths = parallel ? ShortestPathsGraphExtender.FordBellmanShortestPathsParallel : new FordBellmanShortestPaths(ShortestPathsGraphExtender.FordBellmanShortestPaths);

            var gOut = new HashSet <int>();
            var cOut = new HashSet <int>();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                gOut.Clear();
                cOut.Clear();
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.Weight < 0.0)
                    {
                        throw new ArgumentException("Negative capacity edges are not allowed");
                    }
                    if (!g.GetEdgeWeight(edge.To, edge.From).IsNaN())
                    {
                        throw new ArgumentException(
                                  "Edges in both directions between pair of vertices are not allowed");
                    }
                    gOut.Add(edge.To);
                }

                foreach (var edge in c.OutEdges(i))
                {
                    cOut.Add(edge.To);
                }

                if (!gOut.SetEquals(cOut))
                {
                    throw new ArgumentException("Inconsistent capacity and cost graphs");
                }
            }

            var   tempCost = double.NaN;
            var   tempFlow = double.NaN;
            var   maxFlow  = double.NaN;
            Graph flow;

            if (mf != null)
            {
                (maxFlow, flow) = mf(g, source, target, af, matrixToAVL);
            }
            else
            {
                if (!(tempFlow = g.GetEdgeWeight(source, target)).IsNaN())
                {
                    g.DelEdge(source, target);
                }
                if (!(tempCost = c.GetEdgeWeight(source, target)).IsNaN())
                {
                    c.DelEdge(source, target);
                }
                flow = g.IsolatedVerticesGraph();
                var maxPossibleFlow = g.OutEdges(source).Sum(e => e.Weight);
                g.AddEdge(source, target, maxPossibleFlow + 1.0);
                flow.AddEdge(source, target, maxPossibleFlow + 1.0);
                var maxPossibleCost = 0.0;
                for (var i = 0; i < c.VerticesCount; i++)
                {
                    foreach (var edge4 in c.OutEdges(i))
                    {
                        maxPossibleCost += Math.Abs(edge4.Weight);
                        flow.AddEdge(edge4.From, edge4.To, 0.0);
                    }
                }
                c.AddEdge(source, target, maxPossibleCost + 1.0);
            }

            var residualFlow = flow.IsolatedVerticesGraph();
            var residualCost = flow.IsolatedVerticesGraph();

            for (var i = 0; i < flow.VerticesCount; i++)
            {
                foreach (var edge in flow.OutEdges(i))
                {
                    var something = Math.Min(g.GetEdgeWeight(edge.From, edge.To), double.MaxValue) - edge.Weight;
                    if (something > 0.0)
                    {
                        residualFlow.AddEdge(edge.From, edge.To, something);
                        residualCost.AddEdge(edge.From, edge.To, c.GetEdgeWeight(edge.From, edge.To));
                    }

                    if (edge.Weight > 0.0)
                    {
                        residualFlow.AddEdge(edge.To, edge.From, edge.Weight);
                        residualCost.AddEdge(edge.To, edge.From, -c.GetEdgeWeight(edge.From, edge.To));
                    }
                }
            }

            var foundFlow = 0.0;

            while (!fordBellmanShortestPaths(residualCost, target, out var pi))
            {
                var cycle        = residualCost.FindNegativeCostCycle(pi).cycle;
                var cycleMaxFlow = double.PositiveInfinity;
                var flag         = false;
                foreach (var edge in cycle)
                {
                    if (edge.From == target && edge.To == source)
                    {
                        flag = true;
                    }
                    var weight = residualFlow.GetEdgeWeight(edge.From, edge.To);
                    if (cycleMaxFlow > weight)
                    {
                        cycleMaxFlow = weight;
                    }
                }
                if (flag)
                {
                    foundFlow += cycleMaxFlow;
                }
                foreach (var edge in cycle)
                {
                    if (flag = (flow.GetEdgeWeight(edge.To, edge.From) > 0.0))
                    {
                        var weight = flow.ModifyEdgeWeight(edge.To, edge.From, -cycleMaxFlow);
                        if (weight < 0.0)
                        {
                            flow.ModifyEdgeWeight(edge.To, edge.From, -weight);
                            flow.ModifyEdgeWeight(edge.From, edge.To, -weight);
                        }
                    }
                    else
                    {
                        flow.ModifyEdgeWeight(edge.From, edge.To, cycleMaxFlow);
                    }

                    if (residualFlow.ModifyEdgeWeight(edge.From, edge.To, -cycleMaxFlow) == 0.0)
                    {
                        residualFlow.DelEdge(edge);
                        residualCost.DelEdge(edge);
                    }

                    if (residualFlow.ModifyEdgeWeight(edge.To, edge.From, cycleMaxFlow).IsNaN())
                    {
                        residualFlow.AddEdge(edge.To, edge.From, cycleMaxFlow);
                        var weight = (flag ? c.GetEdgeWeight(edge.To, edge.From) : c.GetEdgeWeight(edge.From, edge.To));
                        residualCost.AddEdge(edge.To, edge.From, flag ? weight : (-weight));
                    }
                }
            }
            if (mf == null)
            {
                g.DelEdge(source, target);
                c.DelEdge(source, target);
                flow.DelEdge(source, target);
                if (!tempFlow.IsNaN())
                {
                    foundFlow += tempFlow;
                    g.AddEdge(source, target, tempFlow);
                    c.AddEdge(source, target, tempCost);
                    flow.AddEdge(source, target, tempFlow);
                }
                maxFlow = foundFlow;
            }
            var cost = 0.0;

            for (var k = 0; k < flow.VerticesCount; k++)
            {
                cost += flow.OutEdges(k).Sum(e => e.Weight * c.GetEdgeWeight(e.From, e.To));
            }
            return(maxFlow, cost, flow);
        }
        /// <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)
             */
        }