示例#1
0
文件: Lab03.cs 项目: L-Dogg/ASD2
        public static bool BFSColoring(Graph g, int v, ref COLORS [] col)
        {
            Queue <int> q = new Queue <int>();
            int         vert;
            int         partition_class = 0;
            int         i;

            q.Enqueue(v);

            while (q.Count != 0)
            {
                i    = 0;
                vert = q.Dequeue();
                if (col[vert] == COLORS.NONE || col[vert] == ((partition_class % 2 == 0) ? COLORS.RED : COLORS.BLUE))
                {
                    if (col[vert] == COLORS.NONE)
                    {
                        col[vert] = (partition_class++ % 2 == 0) ? COLORS.RED : COLORS.BLUE;
                    }

                    foreach (Edge e in g.OutEdges(vert))
                    {
                        if (col[e.To] == COLORS.NONE)
                        {
                            if (col[vert] == COLORS.RED)
                            {
                                col[e.To] = COLORS.BLUE;
                            }
                            else
                            {
                                col[e.To] = COLORS.RED;
                            }
                            q.Enqueue(e.To);
                        }
                        else if (col[e.To] == col[vert])
                        {
                            Console.WriteLine("Vertex {0} (Color {1}) is connected with Vertex {2} (Color {3})", e.To, col[e.To], vert, col[vert]);
                            return(false);
                        }
                        else
                        {
                            i++;
                        }
                    }
                    if (i == g.OutDegree(v))
                    {
                        partition_class--;
                    }
                }
                foreach (Edge e in g.OutEdges(vert))
                {
                    if (col[e.To] == col[vert])
                    {
                        return(false);
                    }
                }
                partition_class++;
            }
            return(true);
        }
示例#2
0
            /// <summary>
            /// Bada izomorfizm grafów metodą pełnego przeglądu (rekurencyjnie)
            /// </summary>
            /// <param name="vh">Aktualnie rozważany wierzchołek</param>
            /// <param name="map">Mapowanie wierzchołków grafu h na wierzchołki grafu g</param>
            /// <returns>Informacja czy znaleziono mapowanie definiujące izomotfizm</returns>
            internal bool FindMapping(int vh, int[] map)
            {
                // Wskazówki
                // 1) w sposób systematyczny sprawdzać wszystkie potencjalne mapowania
                // 2) unikać wielokrotnego sprawdzania tego samego mapowania
                // 3) zastosować algorytm z powrotami (backtracking)
                // 4) do badania krawędzi pomiędzy wierzchołkami i oraz j użyć metody GetEdgeWeight(i,j)

                if (vh == g.VerticesCount)
                {
                    bool ok = true;
                    for (int i = 0; i < g.VerticesCount; i++)
                    {
                        foreach (Edge e in g.OutEdges(i))
                        {
                            if (!h.GetEdgeWeight(map[e.From], map[e.To]).HasValue)
                            {
                                ok = false;
                            }
                        }
                    }
                    return(ok);
                }

                for (int i = 0; i < g.VerticesCount; i++)
                {
                    if (used[i] == false)
                    {
                        bool ok = true;
                        foreach (Edge e in g.OutEdges(i))
                        {
                            if (used[e.To] == true)
                            {
                                if (h.GetEdgeWeight(i, map[e.To]).HasValue == false)
                                {
                                    ok = false;
                                    break;
                                }
                            }
                        }
                        if (ok)
                        {
                            used[i] = true;
                            map[vh] = i;
                            if (FindMapping(vh++, map))
                            {
                                return(true);
                            }
                            used[i] = false;
                        }
                    }
                }
                return(false);
            }
        /// <summary>
        /// Bada czy zadane grafy są jednakowe
        /// </summary>
        /// <param name="g">Pierwszy badany graf</param>
        /// <param name="h">Drugi badany graf</param>
        /// <returns>Informacja czy zadane grafy są jednakowe</returns>
        /// <remarks>
        /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.<para/>
        /// Metoda wykonuje obliczenia równolegle w wielu wątkach.
        /// </remarks>
        /// <seealso cref="GraphHelperExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool IsEqualParallel(this Graph g, Graph h)
        {
            if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount)
            {
                return(false);
            }
            if (g.Directed != h.Directed)
            {
                return(false);
            }
            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutDegree(i) != h.OutDegree(i) || g.InDegree(i) != h.InDegree(i))
                {
                    return(false);
                }
            }

            var result = Parallel.For(0, g.VerticesCount, (i, state) =>
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (h.GetEdgeWeight(i, edge.To) != edge.Weight)
                    {
                        state.Stop();
                    }
                }
            });

            return(result.IsCompleted);
        }
示例#4
0
        /// <summary>
        /// Zapisuje graf do pliku
        /// </summary>
        /// <param name="g">Wskazany graf</param>
        /// <param name="path">Nazwa pliku</param>
        /// <remarks>
        /// Graf zapisany jest w postaci XML.<para/>
        /// Nie jest stosowana serializacja, dzięki temu graf może być odtworzony w innej reprezentacji
        /// (jako obiekt innego typu - przy serializacji odtwarzany jest obiekt tego samego typu).
        /// </remarks>
        /// <seealso cref="Graph"/>
        /// <seealso cref="ASD.Graphs"/>
        internal static void Write(Graph g, string path)
        {
            var xmlWriterSettings = new XmlWriterSettings {
                Indent = true, IndentChars = "    "
            };

            using (var xmlWriter = XmlWriter.Create(path, xmlWriterSettings))
            {
                xmlWriter.WriteStartDocument();
                xmlWriter.WriteStartElement("Graph");
                xmlWriter.WriteAttributeString("directed", $"{g.Directed}");
                xmlWriter.WriteAttributeString("verticescount", $"{g.VerticesCount}");
                xmlWriter.WriteStartElement("Edges");
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    foreach (var edge in g.OutEdges(i))
                    {
                        xmlWriter.WriteStartElement("Edge");
                        xmlWriter.WriteAttributeString("from", $"{edge.From}");
                        xmlWriter.WriteAttributeString("to", $"{edge.To}");
                        xmlWriter.WriteAttributeString("weight", $"{edge.Weight:R}");
                        xmlWriter.WriteEndElement();
                    }
                }
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndDocument();
            }
        }
示例#5
0
        /// <summary>
        /// Znajduje rozwiązanie dokładne problemu komiwojażera metodą podziału i ograniczeń
        /// </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>
        /// Metoda przeznaczona jest dla grafów z nieujemnymi wagami krawędzi.<br/>
        /// Uruchomiona dla grafu zawierającego krawędź o wadze ujemnej zgłasza wyjątek <see cref="System.ArgumentException"/>.<br/>
        /// <br/>
        /// Elementy (krawędzie) umieszczone są w tablicy <i>cycle</i> w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<br/>
        /// <br/>
        /// Jeśli w badanym grafie nie istnieje cykl Hamiltona metoda zwraca wartość <b>NaN</b>,
        /// parametr wyjściowy <i>cycle</i> ma wówczas wartość <b>null</b>.<br/>
        /// <br/>
        /// Metodę można stosować dla grafów skierowanych i nieskierowanych.
        /// </remarks>
        public double TSP(Graph g, out Edge[] cycle)
        {
            int n = g.VerticesCount;

            double[,] CostMatrix = new double[n, n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    CostMatrix[i, j] = -1;
                }
            }

            for (int i = 0; i < n; i++)
            {
                foreach (Edge e in g.OutEdges(i))
                {
                    CostMatrix[i, e.To] = e.Weight;
                }
            }

            BNB(CostMatrix, 0, new List <Edge>(), n);
            cycle = solution.ToArray();
            return(best);
        }
示例#6
0
文件: Lab08.cs 项目: L-Dogg/ASD2
        private static int?ConstructCycle(Graph g, out Edge[] edges)
        {
            if (g == null)
            {
                edges = null;
                return(null);
            }
            List <Edge> cycle = new List <Edge>();

            bool[] visited = new bool[g.VerticesCount];
            int    last    = 0;
            int    weight  = 0;

            while (cycle.Count != g.EdgesCount)
            {
                foreach (Edge e in g.OutEdges(last))
                {
                    if (!visited[e.To])
                    {
                        cycle.Add(e);
                        weight       += e.Weight;
                        visited[e.To] = true;
                        last          = e.To;
                        break;
                    }
                }
            }
            edges = cycle.ToArray();
            return(weight);
        }
        /// <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);
        }
示例#8
0
 private void CreateDotFile(Graph g, IReadOnlyList <string> verticesDescriptions, string fileName)
 {
     using (var streamWriter = new StreamWriter(fileName + ".dot"))
     {
         var format = " [label=\"{0" + WeightsFormat + "}\"]";
         streamWriter.WriteLine(g.Directed ? "digraph" : "graph");
         streamWriter.WriteLine("{");
         for (var i = 0; i < g.VerticesCount; i++)
         {
             streamWriter.WriteLine("\tnode [shape=circle, label=\"{0}\"] {1};", verticesDescriptions[i], i);
         }
         for (var i = 0; i < g.VerticesCount; i++)
         {
             foreach (var edge in g.OutEdges(i))
             {
                 if (!g.Directed && edge.From > edge.To)
                 {
                     continue;
                 }
                 streamWriter.Write("\t");
                 streamWriter.Write("{0}", edge.From);
                 streamWriter.Write(g.Directed ? " -> " : " -- ");
                 streamWriter.Write("{0}", edge.To);
                 if (ShowWeights)
                 {
                     IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
                     streamWriter.Write(string.Format(invariantCulture, format, edge.Weight));
                 }
                 streamWriter.WriteLine();
             }
         }
         streamWriter.WriteLine("}");
     }
 }
        /// <summary>
        /// Bada czy zadane grafy są jednakowe
        /// </summary>
        /// <param name="g">Pierwszy badany graf</param>
        /// <param name="h">Drugi badany graf</param>
        /// <returns>Informacja czy zadane grafy są jednakowe</returns>
        /// <remarks>Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.</remarks>
        /// <seealso cref="GraphHelperExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool IsEqual(this Graph g, Graph h)
        {
            if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount)
            {
                return(false);
            }
            if (g.Directed != h.Directed)
            {
                return(false);
            }
            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutDegree(i) != h.OutDegree(i) || g.InDegree(i) != h.InDegree(i))
                {
                    return(false);
                }
            }

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutEdges(i).Any(edge => h.GetEdgeWeight(i, edge.To) != edge.Weight))
                {
                    return(false);
                }
            }

            return(true);
        }
示例#10
0
        private static int CliqueNumberRecursive(Graph g, ref bool[] isVertexInClique, int cliqueSize, int startingVertex = 0)
        {
            bool[] biggestClique     = isVertexInClique;
            int    biggestCliqueSize = cliqueSize;

            if (cliqueSize > 0)
            {
                // Instead of analizying all the vertices
                // we only take into account neighbors of the last vertex in the clique
                // This is especially benefitial for sparse graphs (the last one in the test set)
                foreach (Edge e in g.OutEdges(startingVertex - 1))
                {
                    if (e.To <= e.From)
                    {
                        continue;
                    }

                    CheckNewCliqueVertex(g, e.To, cliqueSize, ref isVertexInClique, ref biggestClique, ref biggestCliqueSize);
                }
            }
            else
            {
                // In the initial step we analyze every vertex
                // because any vertex is a valid clique
                for (int v = startingVertex; v < g.VerticesCount; v++)
                {
                    CheckNewCliqueVertex(g, v, cliqueSize, ref isVertexInClique, ref biggestClique, ref biggestCliqueSize);
                }
            }

            isVertexInClique = biggestClique;
            return(biggestCliqueSize);
        }
示例#11
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);
        }
示例#12
0
        /// <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);
        }
示例#13
0
文件: Lab08.cs 项目: 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);
        }
示例#14
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);
        }
示例#15
0
 private protected void Merge(Graph g)
 {
     for (var i = 0; i < g.VerticesCount; i++)
     {
         foreach (var e in g.OutEdges(i))
         {
             AddEdge(e);
         }
     }
 }
示例#16
0
        /// <summary>
        /// Znajduje rozwiązanie dokładne problemu komiwojażera metodą pełnego przeglądu (backtracking)
        /// </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>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu zawierającego krawędź o wadze ujemnej</exception>
        /// <remarks>
        /// Metoda przeznaczona jest dla grafów z nieujemnymi wagami krawędzi.<para/>
        /// Uruchomiona dla grafu zawierającego krawędź o wadze ujemnej zgłasza wyjątek ArgumentException.
        /// (Warunek ten sprawdzany jest w sposób przybliżony,
        /// jedynie przy próbie dodania krawędzi o ujemnej wadze do konstruowanego cyklu).<para/>
        /// Elementy (krawędzie) umieszczone są w tablicy cycle
        /// w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<para/>
        /// Jeśli w badanym grafie nie istnieje cykl Hamiltona metoda zwraca krotkę (NaN,null).<para/>
        /// Metodę można stosować dla grafów skierowanych i nieskierowanych.
        /// </remarks>
        /// <seealso cref="BacktrackingTSPGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Edge[] cycle) BacktrackingTSP(this Graph g)
        {
            if (g.VerticesCount <= (g.Directed ? 1 : 2))
            {
                return(double.NaN, null);
            }

            Edge[] bestCycle  = null;
            var    bestWeight = double.PositiveInfinity;
            var    tempCycle  = new Edge[g.VerticesCount];
            var    visited    = new bool[g.VerticesCount];

            void Rec(int currVertex, int i, double currWeight)
            {
                if (currWeight >= bestWeight)
                {
                    return;
                }
                if (i == g.VerticesCount - 1)
                {
                    var edgeWeight = g.GetEdgeWeight(currVertex, 0);
                    if (!(currWeight + edgeWeight < bestWeight))
                    {
                        return;
                    }
                    if (edgeWeight < 0.0)
                    {
                        throw new ArgumentException("Negative weights are not allowed");
                    }
                    bestWeight   = currWeight + edgeWeight;
                    tempCycle[i] = new Edge(currVertex, 0, edgeWeight);
                    bestCycle    = (Edge[])tempCycle.Clone();
                    return;
                }
                visited[currVertex] = true;
                foreach (var edge in g.OutEdges(currVertex))
                {
                    if (visited[edge.To])
                    {
                        continue;
                    }
                    if (edge.Weight < 0.0)
                    {
                        throw new ArgumentException("Negative weights are not allowed");
                    }
                    tempCycle[i] = edge;
                    Rec(edge.To, i + 1, currWeight + edge.Weight);
                }
                visited[currVertex] = false;
            }

            Rec(0, 0, 0.0);

            return(double.IsPositiveInfinity(bestWeight) ? (double.NaN, null) : (bestWeight, bestCycle));
        }
示例#17
0
        // koloruje graf algorytmem zachlannym (byc moze niepotymalnie)
        public static int GreedyColor(this Graph g, out int[] colors)
        {
            // kazdemu wierzcholkowi
            // przydzielamy najmniejszy kolor nie kolidujacy z juz pokolorowanymi sasiadami
            // (wpisujemy go do tablicy colors)
            // zwracamy liczbe uzytych kolorow

            int n = g.VerticesCount;

            colors = new int[n];
            if (n == 0)
            {
                return(0);
            }

            int maxColorUsed = 1;

            colors[0] = 0;
            bool[] colorsUsedByNeighbors = new bool[n];
            for (int v = 1; v < n; ++v)
            {
                for (int i = 0; i < n; i++)
                {
                    colorsUsedByNeighbors[i] = false;
                }

                foreach (Edge e in g.OutEdges(v))
                {
                    if (e.To >= v)
                    {
                        continue;
                    }
                    colorsUsedByNeighbors[colors[e.To]] = true;
                }

                int lowestColorForV = -1;
                for (int i = 0; i < n; i++)
                {
                    if (!colorsUsedByNeighbors[i])
                    {
                        lowestColorForV = i;
                        break;
                    }
                }

                colors[v] = lowestColorForV;
                if (lowestColorForV > maxColorUsed)
                {
                    maxColorUsed = lowestColorForV;
                }
            }

            return(maxColorUsed + 1);
        }
示例#18
0
        /// <summary>
        /// Bada czy zadane mapowanie wierzchołków definiuje izomorfizm grafów
        /// </summary>
        /// <param name="g">Pierwszy badany graf</param>
        /// <param name="h">Drugi badany graf</param>
        /// <param name="map">Zadane mapowanie wierzchołków</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Mapowanie wierzchołków zdefiniowane jest w ten sposób,
        /// że wierzchołkowi v w grafie g odpowiada wierzchołek map[v] w grafie h.<para/>
        /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.<para/>
        /// Metoda wykonuje obliczenia równolegle w wielu wątkach.
        /// </remarks>
        /// <seealso cref="IsomorphismGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool IsIsomorphicParallel(this Graph g, Graph h, int[] map)
        {
            if (g.VerticesCount != h.VerticesCount)
            {
                return(false);
            }
            if (g.EdgesCount != h.EdgesCount)
            {
                return(false);
            }
            if (g.Directed != h.Directed)
            {
                return(false);
            }
            if (map == null)
            {
                throw new ArgumentException("Invalid mapping");
            }
            if (map.Length != g.VerticesCount)
            {
                throw new ArgumentException("Invalid mapping");
            }
            var used = new bool[g.VerticesCount];

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (map[i] < 0 || map[i] >= g.VerticesCount || used[map[i]])
                {
                    throw new ArgumentException("Invalid mapping");
                }
                used[map[i]] = true;
            }

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutDegree(i) != h.OutDegree(map[i]) || g.InDegree(i) != h.InDegree(map[i]))
                {
                    return(false);
                }
            }

            var result = Parallel.For(0, g.VerticesCount, (i, state) =>
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.Weight != h.GetEdgeWeight(map[edge.From], map[edge.To]))
                    {
                        state.Stop();
                    }
                }
            });

            return(result.IsCompleted);
        }
示例#19
0
文件: Lab03.cs 项目: 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 !
        }
示例#20
0
        /// <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);
        }
示例#21
0
        /// <summary>
        /// Bada czy zadane mapowanie wierzchołków definiuje izomorfizm grafów
        /// </summary>
        /// <param name="g">Pierwszy badany graf</param>
        /// <param name="h">Drugi badany graf</param>
        /// <param name="map">Zadane mapowanie wierzchołków</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Mapowanie wierzchołków zdefiniowane jest w ten sposób,
        /// że wierzchołkowi v w grafie g odpowiada wierzchołek map[v] w grafie h.<para/>
        /// Badana jest struktura grafu, sposób reprezentacji nie ma znaczenia.
        /// </remarks>
        /// <seealso cref="IsomorphismGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool IsIsomorphic(this Graph g, Graph h, int[] map)
        {
            if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount)
            {
                return(false);
            }
            if (g.Directed != h.Directed)
            {
                return(false);
            }

            if (map == null)
            {
                throw new ArgumentException("Invalid mapping");
            }
            if (map.Length != g.VerticesCount)
            {
                throw new ArgumentException("Invalid mapping");
            }
            var used = new bool[g.VerticesCount];

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (map[i] < 0 || map[i] >= g.VerticesCount || used[map[i]])
                {
                    throw new ArgumentException("Invalid mapping");
                }
                used[map[i]] = true;
            }

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutDegree(i) != h.OutDegree(map[i]) || g.InDegree(i) != h.InDegree(map[i]))
                {
                    return(false);
                }
            }

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (g.OutEdges(i).Any(edge => edge.Weight != h.GetEdgeWeight(map[edge.From], map[edge.To])))
                {
                    return(false);
                }
            }

            return(true);
        }
示例#22
0
文件: Lab05.cs 项目: 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);
        }
示例#23
0
 private bool AreEqual(Graph g1, Graph g2)
 {
     if (g1.VerticesCount != g2.VerticesCount)
     {
         return(false);
     }
     for (int i = 0; i < g1.VerticesCount; ++i)
     {
         var g1e = new HashSet <Edge>(g1.OutEdges(i));
         var g2e = new HashSet <Edge>(g2.OutEdges(i));
         if (!g1e.SetEquals(g2e))
         {
             return(false);
         }
     }
     return(true);
 }
示例#24
0
        /// <summary>
        /// Znajduje rozwiązanie przybliżone problemu komiwojażera naiwnym algorytmem zachłannym
        /// </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 naiwny algorytm zachłanny 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) SimpleGreedyTSP(this Graph g)
        {
            if (g.VerticesCount <= (g.Directed ? 1 : 2))
            {
                return(double.NaN, null);
            }

            var edge    = new Edge(-1, -1, 0.0);
            var visited = new bool[g.VerticesCount];
            var cycle   = new Edge[g.VerticesCount];
            var weight  = 0.0;
            var vertex  = 0;

            for (var i = 0; i < g.VerticesCount - 1;)
            {
                visited[vertex] = true;
                edge            = new Edge(vertex, -1, double.PositiveInfinity);

                foreach (var e in g.OutEdges(vertex))
                {
                    if (!visited[e.To] && edge.Weight > e.Weight)
                    {
                        edge = e;
                    }
                }

                if (edge.To == -1)
                {
                    return(double.NaN, null);
                }

                cycle[i++] = edge;
                weight    += edge.Weight;
                vertex     = edge.To;
            }
            vertex = edge.To;
            edge   = new Edge(vertex, 0, g.GetEdgeWeight(vertex, 0));
            if (edge.Weight.IsNaN())
            {
                return(double.NaN, null);
            }
            cycle[g.VerticesCount - 1] = edge;
            weight += edge.Weight;
            return(weight, cycle);
        }
示例#25
0
        /// <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);
        }
示例#26
0
文件: Lab03.cs 项目: 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);
        }
示例#27
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);
        }
示例#28
0
文件: Lab05.cs 项目: 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);
        }
示例#29
0
            // rekurencyjna metoda znajdujaca najlepsze pokolorowanie
            // v - wierzcholek do pokolorowania
            // colors - tablica kolorow
            // k - maksymalny kolor u¿yty w colors
            internal void Color(int v, int[] colors, int k)
            {
                if (v == n)
                {
                    if (bestColorsNumber > k + 1)
                    {
                        for (int i = 0; i < n; i++)
                        {
                            bestColors[i] = colors[i];
                        }
                        bestColorsNumber = k + 1;
                    }
                    return;
                }


                bool[] colorsUsedByNeighbors = new bool[n];
                foreach (Edge e in g.OutEdges(v))
                {
                    if (e.To >= v)
                    {
                        continue;
                    }
                    colorsUsedByNeighbors[colors[e.To]] = true;
                }

                for (int i = 0; i < bestColorsNumber; i++)
                {
                    if (colorsUsedByNeighbors[i])
                    {
                        continue;
                    }
                    colors[v] = i;
                    Color(v + 1, colors, Math.Max(i, k));
                }
            }
示例#30
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="from">Wierzchołek startowy</param>
        /// <param name="preVisit">Metoda wywoływana przy pierwszym wejściu do wierzchołka</param>
        /// <param name="postVisit">Metoda wywoływana przy ostatecznym opuszczaniu wierzchołka</param>
        /// <param name="visitedVertices">Tablica odwiedzonych wierzchołków</param>
        /// <returns>Informacja czy zbadano wszystkie wierzchołki osiągalne z wierzchołka startowego</returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Metoda odwiedza jedynie te wierzchołki, dla których elementy tablicy visitedVertices mają wartość false
        /// (i zmienia wpisy w visitedVertices na true).<para/>
        /// Tablica visitedVertices musi mieć rozmiar zgodny z liczbą wierzchołków grafu g.<para/>
        /// Domyślna wartość null parametru visitedVertices oznacza tablicę wypełnioną
        /// wartościami false (zostanie ona utworzona wewnątrz metody).<para/>
        /// Metoda oczywiście odwiedza wierzchołki jednokrotnie, zwrot
        /// "ostateczne opuszczenie wierzchołka" oznacza moment,
        /// w którym mechamizm rekursji opuszcza wierzchołek.<para/>
        /// Parametrem metod preVisit i postVisit jest numer odwiedzanego wierzchołka.<para/>
        /// Wyniki zwracane przez metody preVisit i postVisit oznaczają
        /// żądanie kontynuowania (true) lub przerwania (false) obliczeń.<para/>
        /// Parametry preVisit i postVisit mogą mieć wartość null, co oznacza metodę pustą
        /// (nie wykonującą żadnych działań, zwracającą true).<para/>
        /// Jeśli zostały zbadane wszystkie wierzchołki osiągalne z wierzchołka startowego metoda zwraca true,
        /// jeśli obliczenia zostały przerwane przez metody związane z parametrami preVisit lub postVisit
        /// metoda zwraca false.<para/>
        /// Wynik true nie oznacza, że metoda odwiedziła wszystkie wierzchołki grafu
        /// (oznacza jedynie, że odwiedziła wszystkie wierzchołki osiągalne z wierzchołka startowego).<para/>
        /// Uwaga:<para/>
        /// Dla dużych grafów metoda może spowodować przepełnienie stosu
        /// (z powodu zbyt dużej liczby zagnieżdżonych wywołań rekurencyjnych).<para/>
        /// W takim przypadku można zastosować metodę
        /// <see cref="GeneralSearchGraphExtender.GeneralSearchAll{TEdgesContainer}"/> z wykorzystaniem
        /// kontenera <see cref="EdgesStack"/> (jawnego stosu).<para/>
        /// </remarks>
        /// <seealso cref="DFSGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool DFSearchFrom(this Graph g, int from, Predicate <int> preVisit, Predicate <int> postVisit, bool[] visitedVertices = null)
        {
            if (visitedVertices == null)
            {
                visitedVertices = new bool[g.VerticesCount];
            }

            else if (visitedVertices.Length != g.VerticesCount)
            {
                throw new ArgumentException("Invalid visitedVertices length");
            }

            if (visitedVertices[from])
            {
                throw new ArgumentException("Start vertex is already visited");
            }

            if (preVisit == null)
            {
                preVisit = i => true;
            }

            if (postVisit == null)
            {
                postVisit = i => true;
            }

            visitedVertices[from] = true;

            if (!preVisit(from))
            {
                return(false);
            }

            return(!g.OutEdges(from).Any(edge => !visitedVertices[edge.To] && !DFSearchFrom(g, edge.To, preVisit, postVisit, visitedVertices)) && postVisit(from));
        }