/// <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 silnie spójne składowe przy pomocy algorytmu Kosaraju
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>
        /// Krotka (count, scc) składająca się z liczby silnie spójnych składowych i tablicy opisującej te składowe
        /// </returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Tablica scc zawiera informacje do, której silnie spójnej składowej należy dany wierzchołek.
        /// </remarks>
        /// <seealso cref="SCCGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (int count, int[] scc) Kosaraju(this Graph g)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var array     = new int[g.VerticesCount];
            var vertCount = g.VerticesCount;
            var count     = 0;
            var scc       = new int[g.VerticesCount];

            bool PostVisitVertex(int vert)
            {
                count++;
                array[vertCount - count] = vert;
                return(true);
            }

            bool PreVisitVertex(int vert)
            {
                scc[vert] = count - 1;
                return(true);
            }

            g.GeneralSearchAll <EdgesStack>(null, PostVisitVertex, null, out _);
            Reverse(g).GeneralSearchAll <EdgesStack>(PreVisitVertex, null, null, out count, array);
            return(count, scc);
        }
示例#3
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);
        }
示例#4
0
文件: Lab05.cs 项目: L-Dogg/ASD2
        /// <summary>
        /// Bada czy graf nieskierowany jest acykliczny
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Informacja czy graf jest acykliczny</returns>
        /// <remarks>
        /// Metoda uruchomiona dla grafu skierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static bool IsUndirectedAcyclic(this Graph g)
        {
            if (g.Directed == true)
            {
                throw new System.ArgumentException("Should be undirected");
            }

            int cc;

            g.GeneralSearchAll <EdgesStack>(null, null, out cc, null);
            return(cc == (g.VerticesCount - g.EdgesCount));
        }
示例#5
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);
        }
        /// <summary>
        /// Sortowanie topologiczne wierzchołków grafu
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="original2topological">Tablica opisująca przekształcenie numeracji pierwotnej w topologiczną</param>
        /// <param name="topological2original">Tablica opisująca przekształcenie numeracji topologicznej w pierworną</param>
        /// <returns>Informacja czy posortowanie topologiczne jest możliwe</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Wartość original2topological[i] to numer w porządku topologicznym wierzchołka o numerze pierwotnym i.<para/>
        /// Wartość topological2original[i] to numer w porządku pierwotnym wierzchołka o numerze topologicznym i.<para/>
        /// Jeśli posortowanie topologiczne grafu g nie jest możliwe (graf nie jest acykliczny)
        /// to parametry original2topological i topological2original są równe null.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="GraphHelperExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool TopologicalSort(this Graph g, out int[] original2topological, out int[] topological2original)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graph are not allowed");
            }

            var o2t             = new int[g.VerticesCount];
            var t2o             = new int[g.VerticesCount];
            var visitedVertices = new bool[g.VerticesCount];
            var verticesCount   = g.VerticesCount;

            topological2original = null;
            original2topological = null;

            for (var i = 0; i < g.VerticesCount; i++)
            {
                o2t[i] = -1;
            }

            bool PreVisitVertex(int vert)
            {
                visitedVertices[vert] = true;
                return(true);
            }

            bool PostVisitVertex(int vert)
            {
                verticesCount--;
                o2t[vert]          = verticesCount;
                t2o[verticesCount] = vert;
                return(true);
            }

            bool VisitEdge(Edge edge) => !visitedVertices[edge.To] || o2t[edge.To] != -1;

            if (!g.GeneralSearchAll <EdgesStack>(PreVisitVertex, PostVisitVertex, VisitEdge, out _))
            {
                return(false);
            }
            original2topological = o2t;
            topological2original = t2o;
            return(true);
        }
示例#7
0
        /// <summary>
        /// Bada czy graf nieskierowany jest acykliczny
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Informacja czy graf jest acykliczny</returns>
        /// <remarks>
        /// Metoda uruchomiona dla grafu skierowanego zgłasza wyjątek <see cref="System.ArgumentException"/>.
        /// <br/>
        /// Graf wejściowy pozostaje niezmieniony.
        /// </remarks>
        public static bool IsUndirectedAcyclic(this Graph g)
        {
            if (g.Directed)
            {
                throw new System.ArgumentException("Graf jest skierowany");
            }

            bool[]     isVertexInCurrentPath = new bool[g.VerticesCount];
            EdgesStack edgesInCurrentPath    = new EdgesStack();
            bool       isAcyclic             = g.GeneralSearchAll <EdgesStack>(v =>
            {
                isVertexInCurrentPath[v] = true;
                return(true);
            }, v =>
            {
                isVertexInCurrentPath[v] = false;
                return(true);
            }, e =>
            {
                if (!edgesInCurrentPath.Empty && edgesInCurrentPath.Peek().From == e.To)
                {
                    // Krawędź "powrotna" aktualnej ścieżki
                    edgesInCurrentPath.Get();
                    return(true);
                }

                if (isVertexInCurrentPath[e.To])
                {
                    return(false);
                }
                edgesInCurrentPath.Put(e);
                return(true);
            }, out int cc);

            return(isAcyclic);
        }
示例#8
0
        /// <summary>
        /// Bada izomorfizm grafów metodą pełnego przeglądu (backtracking)
        /// </summary>
        /// <param name="g">Pierwszy badany graf</param>
        /// <param name="h">Drugi badany graf</param>
        /// <returns>Znalezione mapowanie wierzchołków</returns>
        /// <remarks>
        /// Jeśli grafy są izomorficzne metoda zwraca mapowanie wierzchołków grafu g na wierzchołki grafu h,
        /// w przeciwnym przypadku metoda zwraca null<para/>
        /// Odpowiedniość wierzchołków zdefiniowana 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 int[] Isomorphism(this Graph g, Graph h)
        {
            if (g.VerticesCount != h.VerticesCount || g.EdgesCount != h.EdgesCount ||
                g.Directed != h.Directed)
            {
                return(null);
            }

            var vertCount = g.VerticesCount;
            var mapping   = new int[vertCount];
            var mapped    = new bool[vertCount];
            var int_2     = new int[vertCount];
            var int_3     = new int[vertCount];

            for (var i = 0; i < vertCount; i++)
            {
                int_3[i] = -1;
            }
            var order   = 0;
            var visited = new bool[vertCount];

            bool PreVisitVertex(int i)
            {
                visited[i]     = true;
                int_2[order++] = i;
                return(true);
            }

            bool VisitEdge(Edge edge)
            {
                if (!visited[edge.To] && int_3[edge.To] == -1)
                {
                    int_3[edge.To] = edge.From;
                }
                return(true);
            }

            g.GeneralSearchAll <EdgesStack>(PreVisitVertex, null, VisitEdge, out _);

            bool FindIsomorphism(int vert)
            {
                (int int_0, int int_1)s;
                s.int_1 = vert;
                if (s.int_1 == vertCount)
                {
                    return(true);
                }

                s.int_0 = int_2[s.int_1];
                if (int_3[s.int_0] != -1)
                {
                    return(h.OutEdges(mapping[int_3[s.int_0]]).Any(edge => Check(edge.To, ref s)));
                }

                for (var i = 0; i < vertCount; i++)
                {
                    if (Check(i, ref s))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            bool Check(int int_4, ref (int int_0, int int_1) pair)
            {
                if (mapped[int_4] || g.OutDegree(pair.int_0) != h.OutDegree(int_4) ||
                    g.InDegree(pair.int_0) != h.InDegree(int_4))
                {
                    return(false);
                }
                for (var i = 0; i < pair.int_1; i++)
                {
                    var w1 = g.GetEdgeWeight(int_2[i], pair.int_0);
                    var w2 = h.GetEdgeWeight(mapping[int_2[i]], int_4);
                    if (w1 != w2 && (!w1.IsNaN() || !w2.IsNaN()))
                    {
                        return(false);
                    }

                    w1 = g.GetEdgeWeight(pair.int_0, int_2[i]);
                    w2 = h.GetEdgeWeight(int_4, mapping[int_2[i]]);
                    if (w1 != w2 && (!w1.IsNaN() || !w2.IsNaN()))
                    {
                        return(false);
                    }
                }
                mapped[int_4]       = true;
                mapping[pair.int_0] = int_4;
                if (FindIsomorphism(pair.int_1 + 1))
                {
                    return(true);
                }

                mapped[int_4] = false;
                return(false);
            }

            return(FindIsomorphism(0) ? mapping : null);
        }
示例#9
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