Пример #1
0
        /// <summary>
        /// Wyznacza ścieżki o maksymalnej przepustowości
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione ścieżki (parametr wyjściowy)</param>
        /// <returns><b>true</b></returns>
        /// <remarks>
        /// Metoda przydaje się w algorytmach wyznaczania maksymalnego przepływu, wagi krawędzi oznaczają tu przepustowość krawędzi.<br/>
        /// Przepustowość ścieżki to najmniejsza z przepustowości krawędzi wchodzących w jej skład.<br/>
        /// <br/>
        /// Elementy tablicy <i>d</i> zawierają przepustowości ścieżek od źródła do wierzchołka określonego przez indeks elementu.<br/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to przepustowość ma wartość <b>null</b>.
        /// <br/>
        /// Metoda zawsze zwraca <b>true</b> (można ją stosować do każdego grafu).
        /// </remarks>
        public static bool MaxFlowPathsLab05(this Graph g, int s, out PathsInfo[] d)
        {
            PathsInfo[] paths = new PathsInfo[g.VerticesCount];

            Predicate <Edge> visitEdge = (Edge e) =>
            {
                int?last = paths[e.From].Dist;
                if (last == null)
                {
                    paths[e.To].Last = e;
                    paths[e.To].Dist = e.Weight;
                    return(true);
                }
                if (paths[e.To].Dist == null || paths[e.To].Dist.Value <= Math.Min(e.Weight, last.Value))
                {
                    paths[e.To].Last = e;
                    paths[e.To].Dist = Math.Min(e.Weight, last.Value);
                }
                return(true);
            };

            g.GeneralSearchFrom <EdgesMaxPriorityQueue>(s, null, visitEdge);
            d = paths;
            return(true);
        }
Пример #2
0
        /// <summary>
        /// Wyznacza silnie spójne składowe
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="scc">Silnie spójne składowe (parametr wyjściowy)</param>
        /// <returns>Liczba silnie spójnych składowych</returns>
        /// <remarks>
        /// scc[v] = numer silnie spójnej składowej do której należy wierzchołek v<br/>
        /// (numerujemy od 0)<br/>
        /// <br/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static int StronglyConnectedComponents(this Graph g, out int[] scc)
        {
            if (!g.Directed)
            {
                throw new System.ArgumentException("Graf jest nieskierowany");
            }

            // Algorytm Kosaraju
            int[] initialOrder      = new int[g.VerticesCount];
            int   currentOrderValue = 0;

            g.GeneralSearchAll <EdgesStack>(null, v =>
            {
                initialOrder[currentOrderValue++] = v;
                return(true);
            }, null, out int cc);

            int[] vertexInComponent = new int[g.VerticesCount];
            Graph reversed          = g.CustomReverse(); // można skorzystać z bibliotecznego g.Reverse, ale zapewne o to chodziło w zadaniu, żeby zrobić własne

            bool[] visited          = new bool[g.VerticesCount];
            int    leftToVisit      = g.VerticesCount;
            int    currentComponent = 0;

            while (leftToVisit > 0)
            {
                int startingVertex = 0;
                for (int i = g.VerticesCount - 1; i >= 0; i--)
                {
                    int v = initialOrder[i];
                    if (!visited[v])
                    {
                        startingVertex = v;
                        break;
                    }
                }

                reversed.GeneralSearchFrom <EdgesStack>(startingVertex, v =>
                {
                    leftToVisit--;
                    vertexInComponent[v] = currentComponent;
                    return(true);
                }, null, null, visited);
                currentComponent++;
            }


            scc = vertexInComponent;
            return(currentComponent);
        }
Пример #3
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);
        }
Пример #4
0
        /// <summary>
        /// Przeszukuje cały graf
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="preVisitVertex">Metoda wywoływana przy pierwszym wejściu do wierzchołka</param>
        /// <param name="postVisitVertex">Metoda wywoływana przy ostatecznym opuszczaniu wierzchołka</param>
        /// <param name="visitEdge">Metoda wywoływana dla odwiedzanych krawędzi</param>
        /// <param name="cc">Liczba "spójnych składowych" grafu (parametr wyjściowy)</param>
        /// <param name="nr">Tablica kolejności "wierzchołków startowych"</param>
        /// <typeparam name="TEdgesContainer">Typ kolekcji przechowującej krawędzie</typeparam>
        /// <returns>Informacja czy zbadano wszystkie krawędzie grafu</returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Metoda odwiedza wszystkie wierzchołki grafu wykorzystując metodę
        /// <see cref="GeneralSearchFrom{TEdgesContainer}"/> (w razie potrzeby wywołuje ją wielokrotnie).<para/>
        /// Jako wierzchołek startowy dla metody <see cref="GeneralSearchFrom{TEdgesContainer}"/>
        /// wybierany jest pierwszy nieodwiedzony wierzchołek nr[i],
        /// gdzie tablica nr przeglądana jest w kierunku rosnących indeksów.<para/>
        /// Domyślna wartość parametru nr (null) oznacza, że tablica nr zostanie automatycznie
        /// wypełniona wartościami nr[i]=i (kolejność domyślna).<para/>
        /// Wartością parametru wyjściowego cc jest wykonana liczba wywołań metody
        /// <see cref="GeneralSearchFrom{TEdgesContainer}"/> (dla grafów nieskierowanych jest to
        /// liczba spójnych składowych grafu, dla grafów skierowanych oczywiście nie ma takiej zależności).<para/>
        /// Wartość parametru cc jest inkrementowana przed każdym wywołaniem metody
        /// <see cref="GeneralSearchFrom{TEdgesContainer}"/>.<para/>
        /// Parametrem metod preVisitVertex i postVisitVertex jest numer odwiedzanego wierzchołka.<para/>
        /// Metoda preVisitVertex jest wywoływana gdy przeszukiwanie grafu osiąga dany wierzchołek
        /// (jest on już oznaczony jako odwiedzony, ale wychodzące z
        /// niego krawędzie nie są jeszcze dodane do kolekcji.<para/>
        /// Metoda postVisitVertex jest wywoływana gdy z kolekcji została usunięta
        /// (i w pełni przetworzona) ostatnia krawędź wychodząca z danego wierzchołka.<para/>
        /// Korzystanie z metody postVisitVertex jest dozwolone jedynie
        /// gdy kontenerem przechowującym krawędzie jest <see cref="EdgesStack"/> czyli
        /// dla przeszukiwania w głąb (DFS) z jawnym stosem
        /// (w innych przypadkach metoda zgłasza wyjątek <see cref="ArgumentException"/>).<para/>
        /// Metoda visitEdge jest wywoływana dla każdej krawędzi rozważanej
        /// podczas przeszukiwania (nawet jeśli nie została wykorzystana),
        /// krawędź ta jest parametrem metody visitEdge.<para/>
        /// Wyniki zwracane przez metody preVisitVertex, postVisitVertex
        /// i visitEdge oznaczają żądanie kontynuowania (true) lub przerwania (false) obliczeń.<para/>
        /// Parametry preVisitVertex, postVisitVertex i visitEdge mogą mieć wartość null,
        /// co oznacza metodę pustą (nie wykonującą żadnych działań, zwracającą true).<para/>
        /// Jeśli zostały zbadane wszystkie krawędzie grafu metoda zwraca true,
        /// jeśli obliczenia zostały przerwane przez metodę związaną z parametrem preVisitVertex,
        /// postVisitVertex lub visitEdge metoda zwraca false.
        /// </remarks>
        /// <seealso cref="GeneralSearchGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool GeneralSearchAll <TEdgesContainer>(this Graph g, Predicate <int> preVisitVertex, Predicate <int> postVisitVertex, Predicate <Edge> visitEdge, out int cc, int[] nr = null) where TEdgesContainer : IEdgesContainer, new()
        {
            if (nr != null)
            {
                if (nr.Length != g.VerticesCount)
                {
                    throw new ArgumentException("Invalid order table");
                }

                var used = new bool[g.VerticesCount];
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    if (nr[i] < 0 || nr[i] >= g.VerticesCount || used[nr[i]])
                    {
                        throw new ArgumentException("Invalid order table");
                    }
                    used[nr[i]] = true;
                }
            }
            else
            {
                nr = new int[g.VerticesCount];
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    nr[i] = i;
                }
            }
            var visitedVertices = new bool[g.VerticesCount];

            cc = 0;
            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (visitedVertices[nr[i]])
                {
                    continue;
                }
                cc++;
                if (!g.GeneralSearchFrom <TEdgesContainer>(nr[i], preVisitVertex, postVisitVertex, visitEdge, visitedVertices))
                {
                    return(false);
                }
            }
            return(true);
        }
Пример #5
0
        /// <summary>Badanie czy graf jest dwudzielny</summary>
        /// <param name="g">Graf wejściowy</param>
        /// <returns>Informacja czy graf wejściowy jest dwudzielny</returns>
        /// <remarks>
        /// 1.5 pkt.
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static bool IsBipartite(this Graph g)
        {
            int verticesCount = g.VerticesCount;

            for (int v = 0; v < verticesCount; v++)
            {
                int[] distanceFromV = new int[verticesCount];
                for (int i = 0; i < verticesCount; i++)
                {
                    distanceFromV[i] = -1;
                }
                distanceFromV[v] = 0;

                Predicate <Edge> updateDistanceFromV = e =>
                {
                    if (distanceFromV[e.To] > -1)
                    {
                        // Cykle muszą być długości parzystej w grafie dwudzielnym
                        if ((distanceFromV[e.To] + distanceFromV[e.From] + 1) % 2 != 0)
                        {
                            return(false);
                        }
                    }

                    distanceFromV[e.To] = distanceFromV[e.From] + 1;
                    return(true);
                };

                if (!g.GeneralSearchFrom <EdgesQueue>(v, null, null, updateDistanceFromV))
                {
                    return(false);
                }
            }

            return(true);
        }
Пример #6
0
        /// <summary>
        /// Wyznacza ścieżkę powiekszającą o maksymalnej przepustowości spośród najkrótszych ścieżek
        /// </summary>
        /// <param name="g">Graf rezydualny</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="t">Wierzchołek docelowy</param>
        /// <returns>
        /// Krotka (augmentingValue, augmentingFlow) składająca się z przepustowości wyznaczonej ścieżki
        /// i grafu opisującego tą ścieżkę
        /// </returns>
        /// <remarks>
        /// Jeśli ścieżka powiększająca nie istnieje, to zwracana jest krotka (0.0,null).<para/>
        /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczona ścieżka powiększająca p jest typu
        /// <see cref="AdjacencyListsGraph{HashTableAdjacencyList}"/>,
        /// w przeciwnym przypadku ścieżka p jest takiego samego typu jak graf g.
        /// </remarks>
        /// <seealso cref="MaxFlowGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double augmentingValue, Graph augmentingFlow) BFMaxPath(this Graph g, int s, int t)
        {
            var pi    = new PathsInfo[g.VerticesCount];
            var steps = new int[g.VerticesCount];

            steps[s]   = 1;
            pi[s].Dist = double.PositiveInfinity;

            bool VisitEdge(Edge e)
            {
                if (steps[e.From] == steps[t])
                {
                    return(false);
                }
                if (steps[e.To] == 0)
                {
                    steps[e.To] = steps[e.From] + 1;
                }
                else if (steps[e.To] != steps[e.From] + 1)
                {
                    return(true);
                }
                var d = Math.Min(pi[e.From].Dist, e.Weight);

                if (!(pi[e.To].Dist < d))
                {
                    return(true);
                }
                pi[e.To].Dist = d;
                pi[e.To].Last = e;
                return(true);
            }

            g.GeneralSearchFrom <EdgesQueue>(s, null, null, VisitEdge);
            return(pi[t].Dist, g.BuildGraph(s, t, pi));
        }