Example #1
0
File: Lab08.cs Project: L-Dogg/ASD2
        }          // 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);
        }
Example #2
0
File: Lab03.cs Project: L-Dogg/ASD2
        /// <summary>Dopełnienie grafu</summary>
        /// <param name="g">Graf wejściowy</param>
        /// <returns>Graf będący dopełnieniem grafu wejściowego</returns>
        /// <remarks>
        /// 0.5 pkt.
        /// Dopełnienie grafu to graf o tym samym zbiorze wierzchołków i zbiorze krawędzi równym VxV-E-"pętle"
        /// Graf wejściowy pozostaje niezmieniony.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga 1 : w przypadku stwierdzenia ze graf wejściowy jest grafem ważonym zgłosić wyjątek ArgumentException
        /// Uwaga 2 : W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph Complement(this Graph g)
        {
            Graph ret = g.IsolatedVerticesGraph();

            HashSet <Edge> elist = new HashSet <Edge>();

            for (int i = 0; i < g.VerticesCount; i++)
            {
                for (int j = 0; j < g.VerticesCount; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    if (g.GetEdgeWeight(i, j) == null)
                    {
                        Edge e = new Edge(i, j, 1);
                        if (!elist.Contains(new Edge(j, i, 1)) && !elist.Contains(e))
                        {
                            ret.AddEdge(i, j);
                            elist.Add(e);
                        }
                    }
                    else if (g.GetEdgeWeight(i, j) > 1)
                    {
                        throw new ArgumentException();
                    }
                }
            }
            return(ret);            // zmienic !
        }
Example #3
0
        /// <summary>Dodawanie wierzchołka do grafu</summary>
        /// <param name="g">Graf, do którego dodajemy wierzchołek</param>
        /// <returns>Graf z dodanym wierzchołkiem</returns>
        /// <remarks>
        /// 0.5 pkt.
        /// Metoda zwraca graf, będący kopią grafu wejściowego z dodanym wierzchołkiem.
        /// Graf wejściowy pozostaje niezmieniony.
        /// W utworzonym grafie są takie same krawędzie jak w wejściowym.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga: W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph AddVertex(this Graph g)
        {
            int   originalVerticesCount = g.VerticesCount;
            Graph newGraph = g.IsolatedVerticesGraph(g.Directed, originalVerticesCount + 1);

            for (int v = 0; v < originalVerticesCount; v++)
            {
                foreach (Edge e in g.OutEdges(v))
                {
                    // W nieskierowanych dodajemy tylko krawędzie od wierzchołka o niższym indeksie
                    // do wierzchołka o wyższym indeksie, żeby uniknąć podwójnego dodawania krawędzi
                    if (!g.Directed)
                    {
                        if (e.From > e.To)
                        {
                            continue;
                        }
                    }

                    newGraph.AddEdge(e);
                }
            }

            return(newGraph);
        }
        /// <summary>
        /// Wyznacza sumę grafów
        /// </summary>
        /// <param name="g">Pierwszy sumowany graf</param>
        /// <param name="h">Drugi sumowany graf</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">Gdy argumentem jest graf skierowany</exception>
        /// <remarks>
        /// Suma grafów to graf składający się ze wszystkich wierchołków i krawędzi sumowanych grafów
        /// (wierzchołki pochodzące z drugiego grafu są odpowiednio przenumerowane).<para/>
        /// Można sumować grafy reprezentowane w różny sposób,
        /// suma ma taką reprezentację jak pierwszy z sumowanych grafów).
        /// </remarks>
        /// <seealso cref="GraphHelperExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static Graph Union(this Graph g, Graph h)
        {
            if (g.Directed != h.Directed)
            {
                throw new ArgumentException("Union of directed and undirected graph are not allowed");
            }

            var union = g.IsolatedVerticesGraph(g.Directed, g.VerticesCount + h.VerticesCount);

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var e in g.OutEdges(i))
                {
                    union.AddEdge(e);
                }
            }

            for (var i = 0; i < h.VerticesCount; i++)
            {
                foreach (var edge in h.OutEdges(i))
                {
                    union.AddEdge(i + g.VerticesCount, edge.To + g.VerticesCount, edge.Weight);
                }
            }

            return(union);
        }
Example #5
0
        /// <summary>Dopełnienie grafu</summary>
        /// <param name="g">Graf wejściowy</param>
        /// <returns>Graf będący dopełnieniem grafu wejściowego</returns>
        /// <remarks>
        /// 0.5 pkt.
        /// Dopełnienie grafu to graf o tym samym zbiorze wierzchołków i zbiorze krawędzi równym VxV-E-"pętle"
        /// Graf wejściowy pozostaje niezmieniony.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga 1 : w przypadku stwierdzenia ze graf wejściowy jest grafem ważonym zgłosić wyjątek ArgumentException
        /// Uwaga 2 : W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph Complement(this Graph g)
        {
            int    verticesCount = g.VerticesCount;
            double edgeWeight    = double.NaN;
            Graph  newGraph      = g.IsolatedVerticesGraph();

            for (int v = 0; v < verticesCount; v++)
            {
                bool[] shouldEdgeBeAdded = new bool[verticesCount];
                if (!g.Directed)
                {
                    // Nie dodajemy krawędzi "do tyłu" w grafach nieskierowanych
                    for (int i = 0; i <= v; i++)
                    {
                        shouldEdgeBeAdded[i] = false;
                    }
                    for (int i = v + 1; i < verticesCount; i++)
                    {
                        shouldEdgeBeAdded[i] = true;
                    }
                }
                else
                {
                    for (int i = 0; i < verticesCount; i++)
                    {
                        shouldEdgeBeAdded[i] = true;
                    }
                    // Nie dodajemy pętli
                    shouldEdgeBeAdded[v] = false;
                }

                foreach (Edge e in g.OutEdges(v))
                {
                    if (edgeWeight.IsNaN())
                    {
                        edgeWeight = e.Weight;
                    }
                    if (edgeWeight != e.Weight)
                    {
                        throw new ArgumentException("Graf jest grafem ważonym.");
                    }

                    shouldEdgeBeAdded[e.To] = false;
                }

                for (int i = 0; i < verticesCount; i++)
                {
                    if (shouldEdgeBeAdded[i])
                    {
                        newGraph.AddEdge(v, i);
                    }
                }
            }

            return(newGraph);
        }
Example #6
0
        /// <summary>
        /// Wyznacza odwrotność grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Odwrotność grafu</returns>
        /// <remarks>
        /// Odwrotność grafu to graf skierowany o wszystkich krawędziach przeciwnie skierowanych niż w grafie pierwotnym.<br/>
        /// <br/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static Graph CustomReverse(this Graph g)
        {
            Graph reversed = g.IsolatedVerticesGraph();

            g.GeneralSearchAll <EdgesQueue>(null, null, e =>
            {
                reversed.AddEdge(e.To, e.From, e.Weight);
                return(true);
            }, out int cc);
            return(reversed);
        }
Example #7
0
File: Lab03.cs Project: L-Dogg/ASD2
        /// <summary>Usuwanie wierzchołka z grafu</summary>\
        /// <param name="g">Graf, z którego usuwamy wierzchołek</param>
        /// <param name="del">Usuwany wierzchołek</param>
        /// <returns>Graf z usunietym wierzchołkiem</returns>
        /// <remarks>
        /// 1.0 pkt.
        /// Metoda zwraca graf, będący kopią grafu wejściowego z usuniętym wierzchołkiem.
        /// Graf wejściowy pozostaje niezmieniony.
        /// W utworzonym grafie są takie same krawędzie jak w wejściowym
        /// (oczywiście z wyjątkiem incydentnych z usuniętym wierzchołkiem, numeracja wierzchołków zostaje zaktualizowana)
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga: W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph DeleteVertex(this Graph g, int del)
        {
            // Czy w ogole istnieje taki wierzcholek:
            if (del > g.VerticesCount)
            {
                return(g);
            }

            // Utworzenie grafu bez krawedzi na bazie wierzcholkow grafu g bez usuwanego:
            Graph ret = g.IsolatedVerticesGraph(g.Directed, g.VerticesCount - 1);

            HashSet <Edge> elist = new HashSet <Edge>();

            // Uzupelnienie wynikowego grafu o krawedzie z uwzglednieniem przesuniecia:
            for (int i = 0; i < g.VerticesCount; i++)
            {
                // Omijamy krawedzie z usuwanego wierzcholka:
                if (i == del)
                {
                    continue;
                }
                foreach (Edge e in g.OutEdges(i))
                {
                    // Omijamy krawedzie z usuwanego wierzcholka:
                    if (e.To == del)
                    {
                        continue;
                    }
                    if (!elist.Contains(new Edge(e.To, e.From, e.Weight)))
                    {
                        if (i < del && e.To < del)
                        {
                            ret.AddEdge(i, e.To, e.Weight);
                        }
                        else if (i < del && e.To > del)
                        {
                            ret.AddEdge(i, e.To - 1, e.Weight);
                        }
                        else if (i > del && e.To < del)
                        {
                            ret.AddEdge(i - 1, e.To, e.Weight);
                        }
                        else
                        {
                            ret.AddEdge(i - 1, e.To - 1, e.Weight);
                        }

                        elist.Add(e);
                    }
                }
            }
            return(ret);            // zmienic !
        }
Example #8
0
File: Lab03.cs Project: L-Dogg/ASD2
        /// <summary>Domknięcie grafu</summary>
        /// <param name="g">Graf wejściowy</param>
        /// <returns>Graf będący domknięciem grafu wejściowego</returns>
        /// <remarks>
        /// 1.5 pkt.
        /// Domknięcie grafu to graf, w którym krawędzią połączone są wierzchołki,
        /// pomiędzy którymi istnieje ścieżka w wyjściowym grafie (pętle wykluczamy).
        /// Graf wejściowy pozostaje niezmieniony.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga 1 : w przypadku stwierdzenia ze graf wejściowy jest grafem ważonym zgłosić wyjątek ArgumentException
        /// </remarks>
        public static Graph Closure(this Graph g)
        {
            Graph ret = g.IsolatedVerticesGraph();

            PathsInfo[]    tab   = new PathsInfo[g.VerticesCount];
            HashSet <Edge> elist = new HashSet <Edge>();

            // Dla każdego wierzchołka i znajdź ścieżki do pozostałych wierzchołków:
            for (int i = 0; i < g.VerticesCount; i++)
            {
                if (g.DijkstraShortestPaths(i, out tab))
                {
                    for (int j = 0; j < g.VerticesCount; j++)
                    {
                        // Pomiń pętle:
                        if (i == j)
                        {
                            continue;
                        }

                        if (g.GetEdgeWeight(i, j) > 1)
                        {
                            throw new ArgumentException();
                        }

                        Edge e = new Edge(i, j, 1);
                        if (tab[j].Dist != null && elist.Contains(new Edge(j, i, 1)) == false && elist.Contains(e) == false)
                        {
                            //Console.WriteLine("Adding edge {0} - {1}", i, j);
                            elist.Add(e);
                            ret.AddEdge(e);
                        }
                    }
                }
            }
            // n = |V|

            /*
             * for i=1 to n
             *      do for j = 1 to n
             *              do if i = j lub (i,j) e E(g)
             *                      then t(i,j) = 1
             *                      else t(i,j) = 0
             * for k = 1 to n
             *      do for i = 1 to n
             *              do for j = 1 to n
             *                      t(i,j) = t(i,j) lub (t(i,k) i t(k,j))
             */
            return(ret);            // zmienic !
        }
Example #9
0
        /// <summary>Domknięcie grafu</summary>
        /// <param name="g">Graf wejściowy</param>
        /// <returns>Graf będący domknięciem grafu wejściowego</returns>
        /// <remarks>
        /// 1.5 pkt.
        /// Domknięcie grafu to graf, w którym krawędzią połączone są wierzchołki,
        /// pomiędzy którymi istnieje ścieżka w wyjściowym grafie (pętle wykluczamy).
        /// Graf wejściowy pozostaje niezmieniony.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga 1 : w przypadku stwierdzenia ze graf wejściowy jest grafem ważonym zgłosić wyjątek ArgumentException
        /// </remarks>
        public static Graph Closure(this Graph g)
        {
            int    verticesCount = g.VerticesCount;
            double edgeWeight    = double.NaN;
            Graph  newGraph      = g.IsolatedVerticesGraph();

            for (int v = 0; v < verticesCount; v++)
            {
                bool[] shouldEdgeBeAdded = new bool[verticesCount];
                for (int i = 0; i < verticesCount; i++)
                {
                    shouldEdgeBeAdded[i] = false;
                }

                Predicate <Edge> updateEdgesToBeAdded = e =>
                {
                    if (edgeWeight.IsNaN())
                    {
                        edgeWeight = e.Weight;
                    }
                    if (edgeWeight != e.Weight)
                    {
                        throw new ArgumentException("Graf jest grafem ważonym.");
                    }

                    // W nieskierowanych nie dodajemy krawędzi "do tyłu"
                    if (g.Directed || e.From < e.To)
                    {
                        shouldEdgeBeAdded[e.To] = true;
                    }

                    return(true);
                };

                g.GeneralSearchFrom <EdgesStack>(v, null, null, updateEdgesToBeAdded);

                // Wykluczamy ewentualną pętlę
                shouldEdgeBeAdded[v] = false;

                for (int i = 0; i < verticesCount; i++)
                {
                    if (shouldEdgeBeAdded[i])
                    {
                        newGraph.AddEdge(v, i);
                    }
                }
            }

            return(newGraph);
        }
Example #10
0
File: Lab05.cs Project: L-Dogg/ASD2
        /// <summary>
        /// Wyznacza odwrotność grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Odwrotność grafu</returns>
        /// <remarks>
        /// Odwrotność grafu to graf skierowany o wszystkich krawędziach przeciwnie skierowanych niż w grafie pierwotnym.<br/>
        /// <br/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static Graph Reverse(this Graph g)
        {
            if (g.Directed == false)
            {
                throw new System.ArgumentException("Graph should be directed");
            }
            Graph ret = g.IsolatedVerticesGraph();

            for (int i = 0; i < g.VerticesCount; i++)
            {
                foreach (Edge e in g.OutEdges(i))
                {
                    ret.AddEdge(e.To, e.From, e.Weight);
                }
            }
            return(ret);
        }
        /// <summary>
        /// Wyznacza odwrotność grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Odwrotność grafu</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Odwrotność grafu to graf skierowany o wszystkich krawędziach
        /// przeciwnie skierowanych niż w grafie pierwotnym.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="SCCGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static Graph Reverse(this Graph g)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var graph = g.IsolatedVerticesGraph();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    graph.AddEdge(edge.To, edge.From, edge.Weight);
                }
            }

            return(graph);
        }
Example #12
0
File: Lab03.cs Project: L-Dogg/ASD2
        /// <summary>Dodawanie wierzchołka do grafu</summary>
        /// <param name="g">Graf, do którego dodajemy wierzchołek</param>
        /// <returns>Graf z dodanym wierzchołkiem</returns>
        /// <remarks>
        /// 0.5 pkt.
        /// Metoda zwraca graf, będący kopią grafu wejściowego z dodanym wierzchołkiem.
        /// Graf wejściowy pozostaje niezmieniony.
        /// W utworzonym grafie są takie same krawędzie jak w wejściowym.
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga: W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph AddVertex(this Graph g)
        {
            Graph          ret   = g.IsolatedVerticesGraph(g.Directed, g.VerticesCount + 1);
            HashSet <Edge> elist = new HashSet <Edge>();

            for (int i = 0; i < g.VerticesCount; i++)
            {
                foreach (Edge e in g.OutEdges(i))
                {
                    if (!elist.Contains(e))
                    {
                        ret.AddEdge(e);
                        elist.Add(e);
                    }
                }
            }

            return(ret);
        }
Example #13
0
        /// <summary>Usuwanie wierzchołka z grafu</summary>
        /// <param name="g">Graf, z którego usuwamy wierzchołek</param>
        /// <param name="del">Usuwany wierzchołek</param>
        /// <returns>Graf z usunietym wierzchołkiem</returns>
        /// <remarks>
        /// 1.0 pkt.
        /// Metoda zwraca graf, będący kopią grafu wejściowego z usuniętym wierzchołkiem.
        /// Graf wejściowy pozostaje niezmieniony.
        /// W utworzonym grafie są takie same krawędzie jak w wejściowym
        /// (oczywiście z wyjątkiem incydentnych z usuniętym wierzchołkiem, numeracja wierzchołków zostaje zaktualizowana)
        /// Utworzony graf ma taką samą reprezentację jak graf wejściowy.
        /// Uwaga: W grafach nieskierowanych nie probować dodawawać po 2 razy tej samej krawędzi
        /// </remarks>
        public static Graph DeleteVertex(this Graph g, int del)
        {
            int   originalVerticesCount = g.VerticesCount;
            Graph newGraph = g.IsolatedVerticesGraph(g.Directed, originalVerticesCount - 1);

            for (int v = 0; v < originalVerticesCount; v++)
            {
                if (v == del)
                {
                    continue;
                }

                foreach (Edge e in g.OutEdges(v))
                {
                    int fromVertex = e.From;
                    int toVertex   = e.To;
                    if (toVertex == del)
                    {
                        continue;
                    }

                    if (fromVertex > del)
                    {
                        fromVertex -= 1;
                    }
                    if (toVertex > del)
                    {
                        toVertex -= 1;
                    }

                    // Podobnie jak przy dodawaniu, krawędź w grafach nieskierowanych dodajemy tylko raz
                    if (!g.Directed && fromVertex > toVertex)
                    {
                        continue;
                    }

                    newGraph.AddEdge(fromVertex, toVertex);
                }
            }

            return(newGraph);
        }
Example #14
0
File: Lab05.cs Project: L-Dogg/ASD2
        /// <summary>
        /// Wyznacza jądro grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Jądro grafu</returns>
        /// <remarks>
        /// Jądro grafu to graf skierowany, którego wierzchołkami są silnie spójne składowe pierwotnego grafu.<br/>
        /// Cała silnie spójna składowa jest "ściśnięta" do jednego wierzchiłka.<br/>
        /// Wierzchołki jądra są połączone krawędzią gdy w grafie pierwotnym połączone są krawędzią dowolne
        /// z wierzchołków odpowiednich składowych (ale nie wprowadzamy pętli !). Wagi krawędzi przyjmujemy równe 1.<br/>
        /// <br/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static Graph Kernel(this Graph g)
        {
            if (g.Directed == false)
            {
                throw new System.ArgumentException("Graph should be directed");
            }

            int[] scc;
            int   n   = g.StronglyConnectedComponents(out scc);
            Graph ret = g.IsolatedVerticesGraph(true, n);

            for (int i = 0; i < scc.Length; i++)
            {
                foreach (Edge e in g.OutEdges(i))
                {
                    if (scc[e.To] != scc[i])
                    {
                        ret.AddEdge(scc[i], scc[e.To]);
                    }
                }
            }
            return(g);
        }
Example #15
0
        /// <summary>
        /// Znajduje rozwiązanie przybliżone problemu komiwojażera algorytmem zachłannym "kruskalopodobnym"
        /// </summary>
        /// <param name="g">Badany graf</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 (krawędzie) umieszczone są w tablicy cycle w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<para/>
        /// Jeśli algorytm "kruskalopodobny" nie znajdzie w badanym grafie cyklu Hamiltona (co oczywiście nie znaczy, że taki cykl nie istnieje) to metoda zwraca krotkę (NaN,null).<para/>
        /// Metodę można stosować dla grafów skierowanych i nieskierowanych.<para/>
        /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi.
        /// </remarks>
        /// <seealso cref="AproxTSPGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Edge[] cycle) KruskalTSP(this Graph g)
        {
            if (g.VerticesCount <= (g.Directed ? 1 : 2))
            {
                return(double.NaN, null);
            }

            var graph                 = !(g is AdjacencyMatrixGraph) ? g.IsolatedVerticesGraph() : new AdjacencyListsGraph <SimpleAdjacencyList>(g.Directed, g.VerticesCount);
            var unionFind             = new UnionFind(g.VerticesCount);
            var edgesMinPriorityQueue = new EdgesMinPriorityQueue();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (g.Directed || edge.From < edge.To)
                    {
                        edgesMinPriorityQueue.Put(edge);
                    }
                }
            }

            while (graph.EdgesCount < g.VerticesCount && !edgesMinPriorityQueue.Empty)
            {
                var edge = edgesMinPriorityQueue.Get();
                if (graph.OutDegree(edge.From) >= (g.Directed ? 1 : 2) ||
                    graph.InDegree(edge.To) >= (g.Directed ? 1 : 2))
                {
                    continue;
                }

                if (unionFind.Union(edge.From, edge.To) || graph.EdgesCount == g.VerticesCount - 1)
                {
                    graph.AddEdge(edge);
                }
            }

            if (graph.EdgesCount < g.VerticesCount)
            {
                return(double.NaN, null);
            }

            var cycle    = new Edge[g.VerticesCount];
            var weight   = 0.0;
            var prevVert = -1;
            var vert     = 0;

            for (var i = 0; i < g.VerticesCount;)
            {
                foreach (var edge in graph.OutEdges(vert))
                {
                    if (edge.To == prevVert)
                    {
                        continue;
                    }
                    cycle[i++] = edge;
                    weight    += edge.Weight;
                    prevVert   = vert;
                    vert       = edge.To;
                    break;
                }
            }
            return(weight, cycle);
        }
Example #16
0
        }  // TSP_Kruskal

        /// <summary>
        /// Znajduje rozwiązanie przybliżone problemu komiwojażera tworząc cykl Hamiltona na podstawie drzewa rozpinającego
        /// </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 bazujący na drzewie rozpinającym 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 nieskierowanych.<br/>
        /// Zastosowana do grafu skierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.<br/>
        /// <br/>
        /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi.<br/>
        /// <br/>
        /// Dla grafu nieskierowanego spełniajacego nierówność trójkąta metoda realizuje algorytm 2-aproksymacyjny.
        /// </remarks>
        public static double TSP_TreeBased(this Graph g, out Edge[] cycle)
        {
            if (g.Directed)
            {
                throw new System.ArgumentException("Graph is directed");
            }

            int n = g.VerticesCount;
            EdgesMinPriorityQueue edgesQueue = new EdgesMinPriorityQueue();

            for (int v = 0; v < n; v++)
            {
                foreach (Edge e in g.OutEdges(v))
                {
                    // Only add edges once
                    if (e.From >= e.To)
                    {
                        continue;
                    }

                    edgesQueue.Put(e);
                }
            }

            Graph     minSpanningTree = g.IsolatedVerticesGraph();
            UnionFind uf = new UnionFind(n);

            while (!edgesQueue.Empty && minSpanningTree.EdgesCount < n - 1)
            {
                Edge e = edgesQueue.Get();
                if (uf.Find(e.From) == uf.Find(e.To))   // Same component, would create a cycle
                {
                    continue;
                }

                minSpanningTree.AddEdge(e);
            }

            if (minSpanningTree.EdgesCount < n - 1)
            {
            }

            int[] preorderVertices = new int[n];
            int   i = 0;

            minSpanningTree.GeneralSearchAll <EdgesQueue>(v =>
            {
                preorderVertices[i++] = v;
                return(true);
            }, null, null, out int cc);

            cycle = new Edge[n];
            double cycleLength = 0;

            for (i = 1; i < n; i++)
            {
                int v1 = preorderVertices[i - 1],
                    v2 = preorderVertices[i];
                Edge e = new Edge(v1, v2, g.GetEdgeWeight(v1, v2));
                cycle[i - 1] = e;
                cycleLength += e.Weight;
                if (e.Weight.IsNaN())
                {
                    break;  // Added an edge that doesn't exist
                }
            }

            if (cycleLength.IsNaN())
            {
                cycle = null;
                return(double.NaN);
            }

            double closingEdgeWeight = g.GetEdgeWeight(preorderVertices[n - 1], preorderVertices[0]);

            cycle[n - 1] = new Edge(preorderVertices[n - 1], preorderVertices[0], closingEdgeWeight);
            cycleLength += closingEdgeWeight;

            return(cycleLength);
        } // TSP_TreeBased
Example #17
0
        /// <summary>
        /// Wyznacza maksymalny przepływ metodą FordaFulkersona lub metodą Dinica
        /// </summary>
        /// <param name="g">Badany graf (sieć przepływowa)</param>
        /// <param name="source">Wierzchołek źródłowy</param>
        /// <param name="target">Wierzchołek docelowy</param>
        /// <param name="af">Metoda powiększania przepływu</param>
        /// <param name="matrixToHashTable">Czy optymalizować sposób reprezentacji grafu rezydualnego</param>
        /// <returns>
        /// Krotka (value, flow) składająca się z wartości maksymalnego przepływu
        /// i grafu opisującego ten przepływ
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli jako parametr af zostanie wybrane wyszukiwanie ścieżki powiększającej to metoda realizuje
        /// algorytm Forda-Fulkersona, jeśli jako parametr af zostanie wybrane wyszukiwanie przepływu blokującego
        /// to metoda realizuje algorytm Dinica.<para/>
        ///  Można oczywiście zdefiniować własną metodę powiększania przepływu zgodną
        /// z typem delegacyjnym <see cref="AugmentFlow"/>.<para/>
        /// Jeśli parametr matrixToHashTable ma wartość true oraz graf g jest typu <see cref="AdjacencyMatrixGraph"/>
        /// (czyli macierz sąsiedztwa) i ma nie więcej niż 10% krawędzi, to wykorzystywany przez algorytm graf
        /// rezydualny jest typu <see cref="AdjacencyListsGraph{HashTableAdjacencyList}"/>.
        /// W przeciwnym przypadku graf rezydualny jest takiego samego typu jak graf g.<para/>
        /// Natomiast jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to inne grafy robocze
        /// (ścieżka powiększająca, przepływ blokujący, graf warstwowy) są typu
        /// <see cref="AdjacencyListsGraph{HashTableAdjacencyList}"/> niezależnie
        /// od wartości parametru matrixToHashTable.<para/>
        /// Wynikowy przepływ maksymalny flow zawsze jest takiego samego typu jak graf g
        /// (niezależnie od wartości parametru matrixToHashTable.<para/>
        /// Jeśli po danej krawędzi nie płynie żaden przepływ,
        /// to nadal jest ona w wynikowym grafie flow (oczywiście z wagą 0).<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego lub grafu
        /// z ujemnymi wagami krawędzi zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy parametry source i target są równe metoda również zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="MaxFlowGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double value, Graph flow) FordFulkersonDinicMaxFlow(this Graph g, int source, int target, AugmentFlow af, bool matrixToHashTable = 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 (af == null)
            {
                throw new ArgumentException("Flow increase method not specified");
            }

            var flow = g.IsolatedVerticesGraph();

            var residual = matrixToHashTable
                ? g is AdjacencyMatrixGraph && 10 * g.EdgesCount < g.VerticesCount * g.VerticesCount
                    ? new AdjacencyListsGraph <HashTableAdjacencyList>(true, g.VerticesCount)
                    : g.IsolatedVerticesGraph()
                : g.IsolatedVerticesGraph();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.Weight < 0.0)
                    {
                        throw new ArgumentException("Negative capacity weights are not allowed");
                    }
                    flow.AddEdge(edge.From, edge.To, 0.0);
                    if (edge.Weight > 0.0)
                    {
                        residual.AddEdge(edge.From, edge.To, Math.Min(edge.Weight, double.MaxValue));
                    }
                }
            }

            var value = 0.0;

            while (true)
            {
                var(augmentingValue, augmentingFlow) = af(residual, source, target);
                if (augmentingValue == 0.0)
                {
                    break;
                }
                value += augmentingValue;
                for (var i = 0; i < augmentingFlow.VerticesCount; i++)
                {
                    foreach (var edge in augmentingFlow.OutEdges(i))
                    {
                        var weight = flow.GetEdgeWeight(edge.To, edge.From);
                        if (weight > 0.0)
                        {
                            weight = flow.ModifyEdgeWeight(edge.To, edge.From, -edge.Weight);
                            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, edge.Weight);
                        }
                        if (residual.ModifyEdgeWeight(edge.From, edge.To, -edge.Weight) == 0.0)
                        {
                            residual.DelEdge(edge);
                        }
                        if (residual.ModifyEdgeWeight(edge.To, edge.From, edge.Weight).IsNaN())
                        {
                            residual.AddEdge(edge.To, edge.From, edge.Weight);
                        }
                    }
                }
            }
            return(value, flow);
        }
        /// <summary>
        /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Kruskala
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>
        /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu
        /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym
        /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/>
        /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli, to umożliwia jednolitą obsługę sytuacji
        /// gdy analizowany graf jest niespójny, wyznaczany jest wówczas las rozpinający.
        /// </remarks>
        /// <seealso cref="MSTGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Graph mst) Kruskal(this Graph g)
        {
            if (g.Directed)
            {
                throw new ArgumentException("Directed graphs are not allowed");
            }

            var unionFind             = new UnionFind(g.VerticesCount);
            var edgesMinPriorityQueue = new EdgesMinPriorityQueue();
            var tree   = g is AdjacencyMatrixGraph ? new AdjacencyListsGraph <AVLAdjacencyList>(false, g.VerticesCount) : g.IsolatedVerticesGraph();
            var weight = 0.0;

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.From < edge.To)
                    {
                        edgesMinPriorityQueue.Put(edge);
                    }
                }
            }

            while (tree.EdgesCount < g.VerticesCount - 1 && !edgesMinPriorityQueue.Empty)
            {
                var edge = edgesMinPriorityQueue.Get();
                if (!unionFind.Union(edge.From, edge.To))
                {
                    continue;
                }
                tree.AddEdge(edge);
                weight += edge.Weight;
            }
            return(weight, tree);
        }
        /// <summary>
        /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Prima
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>
        /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu
        /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym
        /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/>
        /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli, to umożliwia jednolitą obsługę sytuacji
        /// gdy analizowany graf jest niespójny, wyznaczany jest wówczas las rozpinający.
        /// </remarks>
        /// <seealso cref="MSTGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Graph mst) Prim(this Graph g)
        {
            if (g.Directed)
            {
                throw new ArgumentException("Directed graphs are not allowed");
            }

            var tree = g is AdjacencyMatrixGraph ? new AdjacencyListsGraph <AVLAdjacencyList>(false, g.VerticesCount) : g.IsolatedVerticesGraph();

            var weight = 0.0;

            bool VisitEdge(Edge edge)
            {
                if (tree.InDegree(edge.To) != 0)
                {
                    return(true);
                }
                tree.AddEdge(edge);
                weight += edge.Weight;
                return(tree.EdgesCount != g.VerticesCount - 1);
            }

            g.GeneralSearchAll <EdgesMinPriorityQueue>(null, null, VisitEdge, out _);
            return(weight, tree);
        }
        /// <summary>
        /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Boruvki
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>
        /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu
        /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym
        /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/>
        /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli,
        /// to umożliwia jednolitą obsługę sytuacji gdy analizowany graf jest niespójny,
        /// wyznaczany jest wówczas las rozpinający.<para/>
        /// Jest to nieco zmodyfikowana wersja algorytmu Boruvki
        /// (nie ma operacji "sciągania" spójnych składowych w jeden wierzchołek).
        /// </remarks>
        /// <seealso cref="MSTGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Graph mst) Boruvka(this Graph g)
        {
            if (g.Directed)
            {
                throw new ArgumentException("Directed graphs are not allowed");
            }

            var unionFind             = new UnionFind(g.VerticesCount);
            var edgesMinPriorityQueue = new EdgesMinPriorityQueue();
            var tree   = g is AdjacencyMatrixGraph ? new AdjacencyListsGraph <AVLAdjacencyList>(false, g.VerticesCount) : g.IsolatedVerticesGraph();
            var weight = 0.0;
            var change = true;

            while (change)
            {
                change = false;
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    Edge?edge = null;
                    foreach (var e in g.OutEdges(i))
                    {
                        if (unionFind.Find(i) != unionFind.Find(e.To) && (edge == null || e.Weight < edge.Value.Weight))
                        {
                            edge = e;
                        }
                    }
                    if (edge != null)
                    {
                        edgesMinPriorityQueue.Put(edge.Value);
                    }
                }
                while (!edgesMinPriorityQueue.Empty)
                {
                    var edge = edgesMinPriorityQueue.Get();
                    if (!unionFind.Union(edge.From, edge.To))
                    {
                        continue;
                    }
                    tree.AddEdge(edge);
                    weight += edge.Weight;
                    if (tree.EdgesCount == g.VerticesCount - 1)
                    {
                        return(weight, tree);
                    }
                    change = true;
                }
            }
            return(weight, tree);
        }
Example #21
0
        /// <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
Example #22
0
        /// <summary>
        /// Wyznacza jądro grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Jądro grafu</returns>
        /// <remarks>
        /// Jądro grafu to graf skierowany, którego wierzchołkami są silnie spójne składowe pierwotnego grafu.<br/>
        /// Cała silnie spójna składowa jest "ściśnięta" do jednego wierzchiłka.<br/>
        /// Wierzchołki jądra są połączone krawędzią gdy w grafie pierwotnym połączone są krawędzią dowolne
        /// z wierzchołków odpowiednich składowych (ale nie wprowadzamy pętli !). Wagi krawędzi przyjmujemy równe 1.<br/>
        /// <br/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static Graph Kernel(this Graph g)
        {
            if (!g.Directed)
            {
                throw new System.ArgumentException("Graf jest nieskierowany");
            }

            int   componentsCount = g.StronglyConnectedComponents(out int[] vertexInComponent);
            Graph kernel          = g.IsolatedVerticesGraph(g.Directed, componentsCount);

            // Funkcja znajdująca dowolny wierzchołek z wybranej silnie spójnej składowej
            System.Func <int, int> FindVertexFromComponent = componentNumber =>
            {
                for (int v = 0; v < vertexInComponent.Length; v++)
                {
                    if (vertexInComponent[v] == componentNumber)
                    {
                        return(v);
                    }
                }

                return(int.MaxValue);
            };

            for (int componentNumber = 0; componentNumber < componentsCount; componentNumber++)
            {
                // Zaczynając od dowolnego wierzchołka w aktualnie rozpatrywanej silnie spójnej składowej
                // szukamy innych bezpośrednio połączonych silnie spójnych składowych.
                // Czyli przeszukujemy graf aż dojdziemy do wierzchołka, który należy do innej składowej niż
                // ta, z której zaczynaliśmy przeszukiwanie
                int        componentVertex = FindVertexFromComponent(componentNumber);
                bool[]     wasEverInQueue  = new bool[g.VerticesCount];
                EdgesQueue edgesQueue      = new EdgesQueue();

                // Zaczynamy przeszukiwanie
                foreach (Edge e in g.OutEdges(componentVertex))
                {
                    edgesQueue.Put(e);
                }
                wasEverInQueue[componentVertex] = true;

                bool[] isComponentConnected = new bool[componentsCount];

                while (!edgesQueue.Empty)
                {
                    Edge e = edgesQueue.Get();
                    if (vertexInComponent[e.To] == componentNumber)
                    {
                        // Idziemy dalej w tej samej silnie spójnej składowej
                        foreach (Edge outEdge in g.OutEdges(e.To))
                        {
                            if (!wasEverInQueue[outEdge.To])
                            {
                                wasEverInQueue[outEdge.To] = true;
                                edgesQueue.Put(outEdge);
                            }
                        }
                    }
                    else
                    {
                        // Dochodzimy do innej składowej
                        isComponentConnected[vertexInComponent[e.To]] = true;
                    }
                }

                for (int i = 0; i < componentsCount; i++)
                {
                    if (isComponentConnected[i])
                    {
                        kernel.AddEdge(componentNumber, i);
                    }
                }
            }
            return(kernel);
        }
Example #23
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>
        /// 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);
        }
Example #25
0
        /// <summary>
        /// Wyszukiwanie "wąskich gardeł" w sieci przesyłowej
        /// </summary>
        /// <param name="g">Graf przepustowości krawędzi</param>
        /// <param name="c">Graf kosztów rozbudowy sieci (kosztów zwiększenia przepustowości)</param>
        /// <param name="p">Tablica mocy produkcyjnych/zapotrzebowania w poszczególnych węzłach</param>
        /// <param name="flowValue">Maksymalna osiągalna produkcja (parametr wyjściowy)</param>
        /// <param name="cost">Koszt rozbudowy sieci, aby możliwe było osiągnięcie produkcji flowValue (parametr wyjściowy)</param>
        /// <param name="flow">Graf przepływu dla produkcji flowValue (parametr wyjściowy)</param>
        /// <param name="ext">Tablica rozbudowywanych krawędzi (parametr wyjściowy)</param>
        /// <returns>
        /// 0 - zapotrzebowanie można zaspokoić bez konieczności zwiększania przepustowości krawędzi<br/>
        /// 1 - zapotrzebowanie można zaspokoić, ale trzeba zwiększyć przepustowość (niektórych) krawędzi<br/>
        /// 2 - zapotrzebowania nie można zaspokoić (zbyt małe moce produkcyjne lub nieodpowiednia struktura sieci
        ///     - można jedynie zwiększać przepustowości istniejących krawędzi, nie wolno dodawać nowych)
        /// </returns>
        /// <remarks>
        /// Każdy element tablicy p opisuje odpowiadający mu wierzchołek<br/>
        ///    wartość dodatnia oznacza moce produkcyjne (wierzchołek jest źródłem)<br/>
        ///    wartość ujemna oznacza zapotrzebowanie (wierzchołek jest ujściem),
        ///       oczywiście "możliwości pochłaniające" ujścia to moduł wartości elementu<br/>
        ///    "zwykłym" wierzchołkom odpowiada wartość 0 w tablicy p<br/>
        /// <br/>
        /// Jeśli funkcja zwraca 0, to<br/>
        ///    parametr flowValue jest równy modułowi sumy zapotrzebowań<br/>
        ///    parametr cost jest równy 0<br/>
        ///    parametr ext jest pustą (zeroelementową) tablicą<br/>
        /// Jeśli funkcja zwraca 1, to<br/>
        ///    parametr flowValue jest równy modułowi sumy zapotrzebowań<br/>
        ///    parametr cost jest równy sumarycznemu kosztowi rozbudowy sieci (zwiększenia przepustowości krawędzi)<br/>
        ///    parametr ext jest tablicą zawierającą informację o tym o ile należy zwiększyć przepustowości krawędzi<br/>
        /// Jeśli funkcja zwraca 2, to<br/>
        ///    parametr flowValue jest równy maksymalnej możliwej do osiągnięcia produkcji
        ///      (z uwzględnieniem zwiększenia przepustowości)<br/>
        ///    parametr cost jest równy sumarycznemu kosztowi rozbudowy sieci (zwiększenia przepustowości krawędzi)<br/>
        ///    parametr ext jest tablicą zawierającą informację o tym o ile należy zwiększyć przepustowości krawędzi<br/>
        /// Uwaga: parametr ext zawiera informacje jedynie o krawędziach, których przepustowości trzeba zwiększyć
        //     (każdy element tablicy to opis jednej takiej krawędzi)
        /// </remarks>
        public static int BottleNeck(this Graph g, Graph c, int[] p, out int flowValue, out int cost, out Graph flow, out Edge[] ext)
        {
            int   n       = g.VerticesCount;
            Graph network = g.IsolatedVerticesGraph(true, 2 * n + 2);
            Graph costs   = network.IsolatedVerticesGraph();
            // 0, ..., n - 1 - regular vertices
            // n, ..., 2n - 1 - additional vertices (used for extending the network)
            int s = 2 * n;
            int t = 2 * n + 1;


            double totalDemand = 0;

            for (int v = 0; v < n; v++)
            {
                if (p[v] > 0)
                {
                    // v is a source
                    network.AddEdge(s, v, p[v]);
                    costs.AddEdge(s, v, 0);
                }
                else if (p[v] < 0)
                {
                    // v consumes the flow
                    network.AddEdge(v, t, -p[v]);
                    costs.AddEdge(v, t, 0);
                    totalDemand += -p[v];
                }

                foreach (Edge e in g.OutEdges(v))
                {
                    // x - y
                    network.AddEdge(e);
                    costs.AddEdge(v, e.To, 0);

                    // x - x2
                    network.AddEdge(v, n + v, int.MaxValue);
                    costs.AddEdge(v, n + v, c.GetEdgeWeight(v, e.To));

                    // x2 - y
                    network.AddEdge(n + v, e.To, int.MaxValue);
                    costs.AddEdge(n + v, e.To, 0);
                }
            }


            double networkFlowValue = network.MinCostFlow(costs, s, t, out double networkCost, out Graph networkFlow);

            flowValue = Convert.ToInt32(networkFlowValue);
            cost      = Convert.ToInt32(networkCost);

            List <Edge> extensionEdges = new List <Edge>();

            flow = g.IsolatedVerticesGraph();
            for (int v = 0; v < n; v++)
            {
                foreach (Edge e in g.OutEdges(v))
                {
                    double totalFlow    = networkFlow.GetEdgeWeight(v, e.To);
                    double extendedFlow = networkFlow.GetEdgeWeight(n + v, e.To);

                    if (!extendedFlow.IsNaN() && extendedFlow > 0)
                    {
                        totalFlow += extendedFlow;
                        extensionEdges.Add(new Edge(v, e.To, extendedFlow));
                    }

                    flow.AddEdge(v, e.To, totalFlow);
                }
            }

            ext = extensionEdges.ToArray();

            if (extensionEdges.Count == 0)
            {
                return(0);
            }

            return(flowValue == totalDemand ? 1 : 2);
        }