Пример #1
0
        private static void CloseCycle(Graph g, ref Graph tree)
        {
            if (tree == null)
            {
                return;
            }
            int first = -1, last = -1;

            for (int i = 0; i < tree.VerticesCount; i++)
            {
                if (tree.InDegree(i) == 0)
                {
                    first = i;
                }
                if (tree.OutDegree(i) == 0)
                {
                    last = i;
                }
            }
            if (first == -1 || last == -1)
            {
                tree = null;
                return;
            }
            int?weight = g.GetEdgeWeight(last, first);

            if (weight.HasValue)
            {
                tree.AddEdge(last, first, weight.Value);
            }
            else
            {
                tree = null;
            }
        }
        /// <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);
        }
Пример #3
0
        }          // 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);
        }
        /// <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);
        }
Пример #5
0
        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);
        }
Пример #6
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);
        }
Пример #7
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);
        }
Пример #8
0
        // Helper method so as not to duplicate code in the CliqueNumberRecursive method
        private static void CheckNewCliqueVertex(Graph g, int v, int cliqueSize, ref bool[] isVertexInClique, ref bool[] biggestClique, ref int biggestCliqueSize)
        {
            // If vertex has a lower degree than there are already vertices in the clique
            // it definitely cannot be added to the clique
            if (g.OutDegree(v) < cliqueSize || g.InDegree(v) < cliqueSize)
            {
                return;
            }

            // Check if there are edges between this vertex (v) and all the vertices already in the clique
            bool validVertex = true;

            for (int cliqueVertex = 0; cliqueVertex < v; cliqueVertex++)
            {
                if (isVertexInClique[cliqueVertex] && (g.GetEdgeWeight(v, cliqueVertex).IsNaN() || g.GetEdgeWeight(cliqueVertex, v).IsNaN()))
                {
                    validVertex = false;
                    break;
                }
            }

            if (!validVertex)
            {
                return;
            }

            isVertexInClique[v] = true;
            bool[] newClique     = isVertexInClique.Clone() as bool[];
            int    newCliqueSize = CliqueNumberRecursive(g, ref newClique, cliqueSize + 1, v + 1);

            if (newCliqueSize > biggestCliqueSize)
            {
                biggestClique     = newClique;
                biggestCliqueSize = newCliqueSize;
            }
            isVertexInClique[v] = false;
        }
        public static List <int> ConstructHamiltonianPath(Graph spanning)
        {
            prohibitedVertices = new List <int>();
            spanningTree       = spanning;
            int n_vertices = spanningTree.VerticesCount;

            // znajdz pierwszy mozliwy wierzcholek ze stopniem wiekszym niz 2
            // Jesli nie ma takiego wierzcholka (drzewo jest sciezka bez rozgalezien) - wez pierwszy wierzcholek
            int notLeafNum = 0;

            for (int i = 0; i < n_vertices; i++)
            {
                if (spanningTree.OutDegree(i) > 2)
                {
                    notLeafNum = i;
                    break;
                }
            }

            // Ekslorujemy wokól tego wierzcholka. Ten wierzcholek to nasze v.
            var res = Merge(notLeafNum, null, null).hamiltionianPath;

            return(res);
        }
Пример #10
0
        /// <summary>
        /// Przeszukuje graf począwszy od wskazanego wierzchołka
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="from">Wierzchołek startowy</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="visitedVertices">Tablica odwiedzonych wierzchołków</param>
        /// <typeparam name="TEdgesContainer">Typ kolekcji przechowującej krawędzie</typeparam>
        /// <returns>Informacja czy zbadano wszystkie krawędzie 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/>
        /// 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ż ozmaczony 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 osiągalne z wierzchołka startowego metoda zwraca true,
        /// jeśli obliczenia zostały przerwane przez metodę związaną z parametrem preVisitVertex,
        /// postVisitVertex lub visitEdge metoda zwraca false.<para/>
        /// Wynik true nie oznacza, że metoda odwiedziła wszystkie krawędzie grafu
        /// (oznacza jedynie, że odwiedziła wszystkie krawędzie osiągalne z wierzchołka startowego).
        /// </remarks>
        /// <seealso cref="GeneralSearchGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool GeneralSearchFrom <TEdgesContainer>(this Graph g, int from, Predicate <int> preVisitVertex, Predicate <int> postVisitVertex, Predicate <Edge> visitEdge, bool[] visitedVertices = null) where TEdgesContainer : IEdgesContainer, new()
        {
            if (postVisitVertex != null && typeof(TEdgesContainer) != typeof(EdgesStack))
            {
                throw new ArgumentException("Parameter postVisitVertex must be null for containers other than EdgesStack");
            }

            var stack          = new Stack <int>();
            var edgesContainer = Activator.CreateInstance <TEdgesContainer>();

            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 (visitEdge == null)
            {
                visitEdge = edge => true;
            }

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

            visitedVertices[from] = true;
            stack.Push(from);

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

            if (postVisitVertex != null && g.OutDegree(from) == 0)
            {
                return(postVisitVertex(from));
            }

            foreach (var edge in g.OutEdges(from))
            {
                edgesContainer.Put(edge);
            }

            while (true)
            {
                if (edgesContainer.Empty)
                {
                    return(true);
                }

                var edge = edgesContainer.Get();

                if (!visitEdge(edge))
                {
                    return(false);
                }

                if (!visitedVertices[edge.To])
                {
                    visitedVertices[edge.To] = true;
                    stack.Push(edge.To);

                    if (!preVisitVertex(edge.To))
                    {
                        break;
                    }

                    foreach (var e in g.OutEdges(edge.To))
                    {
                        edgesContainer.Put(e);
                    }
                }

                if (postVisitVertex == null)
                {
                    continue;
                }
                while (edgesContainer.Empty || stack.Peek() != edgesContainer.Peek().From)
                {
                    if (!postVisitVertex(stack.Pop()))
                    {
                        return(false);
                    }
                    if (stack.Count == 0)
                    {
                        break;
                    }
                }
            }
            return(false);
        }
Пример #11
0
        /// <summary>
        /// Znajduje scieżkę Eulera w grafie
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="ec">Znaleziona ścieżka (parametr wyjściowy)</param>
        /// <returns>Informacja czy ścieżka Eulera istnieje</returns>
        /// <remarks>
        /// Jeśli w badanym grafie nie istnieje ścieżka Eulera metoda zwraca <b>false</b>, parametr <i>ec</i> ma wówczas wartość <b>null</b>.<br/>
        /// <br/>
        /// Metoda nie modyfikuje badanego grafu.<br/>
        /// <br/>
        /// Metoda implementuje algorytm Fleury'ego.
        /// </remarks>
        public static bool Lab04_Euler(this Graph g, out Edge[] ec)
        {
            // tylko cykl     - 2 pkt
            // cykl i sciezka - 3 pkt

            /*
             * Algorytm Fleury'ego
             *
             * utworz pusty stos krawedzi Euler
             * utworz pusty stos krawedzi pom
             * w = dowolny wierzcholek grafu
             * umiesc na stosie pom sztuczna krawedz <w,w>
             * dopoki pom jest niepusty powtarzaj
             *  w = wierzch. koncowy krawedzi ze szczytu stosu pom (bez pobierania krawedzi ze stosu)
             *  jesli stopien wychodzacy w > 0
             *      e = dowolna krawedz wychodzaca z w
             *      umiesc krawedz e na stosie pom
             *      usun krawedz e z grafu
             *  w przeciwnym przypadku
             *      pobiez szczytowy element ze stosu pom i umiesc go na stosie Euler
             * usun ze stosu Euler sztuczna krawedz (petle) startowa (jest na szczycie)
             *
             * wynik: krawedzie tworzace cykl sa na stosie Euler
             *
             * Uwaga: powyzszy algorytm znajduje cykl Eulera (jesli istnieje),
             *     aby znalezc sciezke nalezy najpierw wyznaczyc wierzcholek startowy
             *     (nie mozna wystartowac z dowolnego)
             */
            EdgesStack Euler = new EdgesStack();
            EdgesStack pom   = new EdgesStack();
            int        w     = 0;

            pom.Put(new Edge(w, w));
            Graph clonedGraph = g.Clone();

            // Trzeba byłoby sprawdzić jeszcze czy g jest Eulerowski (czy wszystkie wierzchołki są
            // stopnia parzystego)

            while (!pom.Empty)
            {
                w = pom.Peek().To;
                if (clonedGraph.OutDegree(w) > 0)
                {
                    Edge e = clonedGraph.OutEdges(w).First();
                    pom.Put(e);
                    clonedGraph.DelEdge(e);
                }
                else
                {
                    Euler.Put(pom.Get());
                }
            }
            Euler.Get();

            ec = Euler.ToArray();
            if (ec.First().From == ec.Last().To)
            {
                return(true);
            }
            else
            {
                ec = null;
                return(false);
            }
        }
Пример #12
0
        /// <summary>
        /// Znajduje scieżkę Eulera w grafie
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="ec">Znaleziona ścieżka (parametr wyjściowy)</param>
        /// <returns>Informacja czy ścieżka Eulera istnieje</returns>
        /// <remarks>
        /// Jeśli w badanym grafie nie istnieje ścieżka Eulera metoda zwraca <b>false</b>, parametr <i>ec</i> ma wówczas wartość <b>null</b>.<br/>
        /// <br/>
        /// Metoda nie modyfikuje badanego grafu.<br/>
        /// <br/>
        /// Metoda implementuje algorytm Fleury'ego.
        /// </remarks>
        public static bool Lab04_Euler(this Graph g, out Edge[] ec)
        {
            // tylko cykl     - 2 pkt
            // cykl i sciezka - 3 pkt

            //check if graph has euler cycle or path
            int numOfOddVertices = 0, cc = 0;
            int oddVertex = -1;

            Predicate <int> preVertex = delegate(int vertex)
            {
                if (g.OutDegree(vertex) % 2 != 0)
                {
                    numOfOddVertices++;
                    oddVertex = vertex;
                    if (numOfOddVertices > 2)
                    {
                        return(false);
                    }
                }

                return(true);
            };

            GeneralSearchGraphExtender.GeneralSearchAll <EdgesStack>(g, preVertex, null, null, out cc);

            if (cc != 1 || numOfOddVertices > 2)
            {
                ec = null;
                return(false);
            }

            Graph h = g.Clone();

            //find euler cycle
            //if(0 == numOfOddVertices)
            //{
            EdgesStack euler = new EdgesStack();
            EdgesStack tmp   = new EdgesStack();

            int v = -1 == oddVertex ? 0 : oddVertex; //h.OutDegree(0) > 0, if start with oddVertex algorithm finds euler path

            tmp.Put(new Edge(v, v));                 //dummy edge to remove later
            while (!tmp.Empty)
            {
                v = tmp.Peek().To;
                if (h.OutDegree(v) > 0)
                {
                    foreach (var e in h.OutEdges(v))
                    {
                        tmp.Put(e);
                        h.DelEdge(e);
                        break;
                    }
                }
                else
                {
                    euler.Put(tmp.Get());
                }
            }

            euler.Get();        //dummy edge

            ec = new Edge[euler.Count];
            for (int i = 0; i < ec.Length; i++)
            {
                ec[i] = euler.Get();
            }
            return(true);
            //}

            /*
             * Algorytm Fleury'ego
             *
             * utworz pusty stos krawedzi Euler
             * utworz pusty stos krawedzi pom
             * w = dowolny wierzcholek grafu
             * umiesc na stosie pom sztuczna krawedz <w,w>
             * dopoki pom jest niepusty powtarzaj
             *  w = wierzch. koncowy krawedzi ze szczytu stosu pom (bez pobierania krawedzi ze stosu)
             *  jesli stopien wychodzacy w > 0
             *      e = dowolna krawedz wychodzaca z w
             *      umiesc krawedz e na stosie pom
             *      usun krawedz e z grafu
             *  w przeciwnym przypadku
             *      pobiez szczytowy element ze stosu pom i umiesc go na stosie Euler
             * usun ze stosu Euler sztuczna krawedz (petle) startowa (jest na szczycie)
             *
             * wynik: krawedzie tworzace cykl sa na stosie Euler
             *
             * Uwaga: powyzszy algorytm znajduje cykl Eulera (jesli istnieje),
             *     aby znalezc sciezke nalezy najpierw wyznaczyc wierzcholek startowy
             *     (nie mozna wystartowac z dowolnego)
             */
        }
Пример #13
0
            /// <summary>
            /// Bada izomorfizm grafów metodą pełnego przeglądu (rekurencyjnie)
            /// </summary>
            /// <param name="currentV">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 currentV, int[] map, bool[] isVertexInMapping)
            {
                // 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)

                // We assume that vertices 0 - (currentV - 1) are already mapped
                int n = g.VerticesCount;

                if (currentV >= n)
                {
                    return(true);
                }

                // I assume that map[i] = j means, that vertex i in graph G is isomorphic with vertex j in graph H

                // We check every possible vertex from graph H that is not yet used
                // and try to match it with currentV (from graph G)
                for (int vH = 0; vH < n; vH++)
                {
                    if (isVertexInMapping[vH])
                    {
                        continue;
                    }
                    if (g.OutDegree(currentV) != h.OutDegree(vH) || g.InDegree(currentV) != h.InDegree(vH))
                    {
                        continue;
                    }

                    // Check every neighbor of currentV that is already in mapping, if it matches neighbors of vH
                    map[currentV] = vH;
                    bool validMatch = true;
                    foreach (Edge eG in g.OutEdges(currentV))
                    {
                        // Edge leads to a neighbor that is not yet in the mapping (because we already analyzed vertices 0 - (currentVertex - 1))
                        if (eG.To > currentV)
                        {
                            continue;
                        }

                        // Edge weights should be equal in both graphs
                        double edgeWeightH = h.GetEdgeWeight(vH, map[eG.To]);
                        if (eG.Weight != edgeWeightH)
                        {
                            validMatch = false;
                            break;
                        }

                        // The same goes for reverse edges
                        double reverseEdgeWeightG = g.GetEdgeWeight(eG.To, currentV);
                        double reverseEdgeWeightH = h.GetEdgeWeight(map[eG.To], vH);
                        if ((!reverseEdgeWeightG.IsNaN() || !reverseEdgeWeightH.IsNaN()) && reverseEdgeWeightG != reverseEdgeWeightH)
                        {
                            validMatch = false;
                            break;
                        }
                    }
                    if (!validMatch)
                    {
                        continue;
                    }

                    // vH in H is isomorphic with currentV in G
                    isVertexInMapping[vH] = true;
                    bool isomorphismFound = FindMapping(currentV + 1, map, isVertexInMapping);
                    if (isomorphismFound)
                    {
                        return(true);
                    }

                    isVertexInMapping[vH] = false;

                    // Isomorphism with this matching not found, look for other possibilities of vertices isomorphic with currentV
                }

                // No isomorphism found
                return(false);
            }
Пример #14
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);
        }
Пример #15
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
        /// <summary>
        /// Znajduje scieżkę Eulera w grafie
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="ec">Znaleziona ścieżka (parametr wyjściowy)</param>
        /// <returns>Informacja czy ścieżka Eulera istnieje</returns>
        /// <remarks>
        /// Jeśli w badanym grafie nie istnieje ścieżka Eulera metoda zwraca false,
        /// parametr ec ma wówczas wartość null.<para/>
        /// Metoda nie modyfikuje badanego grafu.<para/>
        /// Metoda implementuje algorytm Fleury'ego.
        /// </remarks>
        /// <seealso cref="EulerPathGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool EulerPath(this Graph g, out Edge[] ec)
        {
            ec = null;
            var oddDegreeCounter    = 0;
            var startVertex         = 0;
            var hasOutGreaterThanIn = false;
            var hasInGreaterThanOut = false;

            if (g.Directed)
            {
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    var outDegree = g.OutDegree(i);
                    var inDegree  = g.InDegree(i);
                    if (Math.Abs(outDegree - inDegree) > 1)
                    {
                        return(false);
                    }
                    if (outDegree > inDegree)
                    {
                        if (hasOutGreaterThanIn)
                        {
                            return(false);
                        }
                        startVertex         = i;
                        hasOutGreaterThanIn = true;
                    }
                    if (inDegree > outDegree)
                    {
                        if (hasInGreaterThanOut)
                        {
                            return(false);
                        }
                        hasInGreaterThanOut = true;
                    }
                }
            }
            else
            {
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    if ((g.OutDegree(i) & 1) != 1)
                    {
                        continue;
                    }
                    startVertex = i;
                    if (++oddDegreeCounter > 2)
                    {
                        return(false);
                    }
                }
            }
            var visited = new bool[g.VerticesCount];
            var graph   = g.Clone();
            var s1      = new EdgesStack();
            var s2      = new EdgesStack();

            s2.Put(new Edge(startVertex, startVertex));
            while (!s2.Empty)
            {
                var vertex = s2.Peek().To;
                visited[vertex] = true;
                if (graph.OutDegree(vertex) > 0)
                {
                    var edge = graph.OutEdges(vertex).First();
                    s2.Put(edge);
                    graph.DelEdge(edge);
                }
                else
                {
                    s1.Put(s2.Get());
                }
            }
            s1.Get();

            if (graph.EdgesCount > 0)
            {
                return(false);
            }

            for (var i = 0; i < g.VerticesCount; i++)
            {
                if (!visited[i])
                {
                    return(false);
                }
            }

            ec = s1.ToArray();
            return(true);
        }
        /// <summary>
        /// i - obecny wierzcholek, v
        /// j - obecny kierunek, w którym idziemy
        /// prohibited - wierzcholek z którego wlasnie przyszlismy, wiec nie mozemy juz tam pójsc
        /// </summary>
        static MergeRet Merge(int i, int?j, int?prohibited)
        {
            // ten przypadek dzieje sie, gdy jestesmy na jakichs rozgalezieniach
            if (j == null)
            {
                // Kierunki, w które mozemy pójsc z aktualnego wierzcholka
                var dirs = spanningTree.OutEdges(i).Select(x => x.To);

                // Trzeba zabronic chodzenia w kierunki, gdzie juz bylismy (od których zaczynalismy).
                //Do tego sluzy argument "prohibited"
                List <MergeRet> toBind = new List <MergeRet>();

                foreach (var dir in dirs)
                {
                    if (!(prohibited.HasValue && dir == prohibited.Value))
                    {
                        toBind.Add(Merge(i, dir, null));
                    }
                }

                List <int> hampath = Bind(toBind, i);
                return(new MergeRet {
                    hamiltionianPath = hampath, v_i = i, x_i = hampath[1]
                });
            }
            else
            {
                // j nie jest nullem - jestesmy w czesci grafu, gdzie idziemy w jakims kierunku
                // sprawdzamy, czy ta część grafu jest ścieżką (e.g. i-j-k-l).
                // Jeśli tak, zwracany ścieżkę Hamiltona od i do j
                // Jeśli nie, idziemy rekurencujnie od i-tego wierzchołka (ten i-ty wierzchołek będzie naszym v).
                // in this case we will need to prevent returning to previously seen vertices

                var verticesList = new List <int>();

                int curr = j.Value;
                int prev = i;


                verticesList.Add(curr);

                int branchLength = 1;

                while (true)
                {
                    if (spanningTree.OutDegree(curr) == 1)
                    {
                        break;
                    }
                    else if (spanningTree.OutDegree(curr) > 2)
                    {
                        var forward  = new List <int>();
                        var backward = new List <int>();

                        if (verticesList.Count() == 1)
                        {
                            if (!prohibitedVertices.Contains(i))
                            {
                                forward.Add(i);
                                prohibitedVertices.Add(i);
                            }
                        }
                        else
                        {
                            if (verticesList.Count % 2 == 0)
                            {
                                for (int k = 1; k < verticesList.Count - 2; k += 2)
                                {
                                    forward.Add(verticesList[k]);
                                }

                                forward.Add(verticesList[verticesList.Count - 2]);

                                for (int k = verticesList.Count - 4; k > -1; k -= 2)
                                {
                                    backward.Add(verticesList[k]);
                                }
                            }
                            else
                            {
                                for (int k = 1; k < verticesList.Count; k += 2)
                                {
                                    forward.Add(verticesList[k]);
                                }

                                for (int k = verticesList.Count - 3; k > -1; k -= 2)
                                {
                                    backward.Add(verticesList[k]);
                                }
                            }
                        }

                        // return forward U merge U back
                        var merge = Merge(curr, null, prev);
                        merge.hamiltionianPath = forward.Concat(merge.hamiltionianPath).Concat(backward).ToList();

                        return(merge);
                    }

                    int tmp = spanningTree.OutEdges(curr).Where(x => x.To != prev).ToArray()[0].To;
                    prev = curr;
                    curr = tmp;

                    branchLength++;
                    verticesList.Add(curr);
                }

                // spanningTree.OutDegree(curr) == 1
                // laczymy v_i z x_i
                // Mozliwe przypadki:
                // 1. Jest jedynie v_i - w tym etapie nic nie laczymy, bo za wczesnie
                // 2. Jest v_i i x_i - w tym etapie nic nie laczymy, bo za wczesnie
                // 3. Oprócz v_i i x_i sa jeszcze inne wierzcholki - laczymy

                if (branchLength == 1)
                {
                    return(new MergeRet {
                        v_i = curr, x_i = null, hamiltionianPath = null
                    });
                }
                else if (branchLength == 2)
                {
                    return(new MergeRet {
                        v_i = prev, x_i = curr, hamiltionianPath = null
                    });
                }
                else
                {
                    MergeRet   ret      = new MergeRet();
                    List <int> hamCycle = new List <int>();

                    ret.v_i = verticesList[0];
                    ret.x_i = verticesList[1];

                    int numVert = verticesList.Count();

                    //forward
                    for (int k = 1; k < numVert; k += 2)
                    {
                        hamCycle.Add(verticesList[k]);
                    }

                    //backward
                    for (int k = ((numVert - 1) / 2) * 2; k > -1; k -= 2)
                    {
                        hamCycle.Add(verticesList[k]);
                    }

                    ret.hamiltionianPath = hamCycle;
                    return(ret);
                }
            }
        }