Exemple #1
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>
        /// Wyznacza minimalne drzewo rozpinające grafu algorytmem Kruskala
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>
        /// Krotka (weight, mst) składająca się z wagi minimalnego drzewa rozpinającego i grafu opisującego to drzewo
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to wyznaczone drzewo rozpinające mst jest typu
        /// <see cref="AdjacencyListsGraph{AVLAdjacencyList}"/>, w przeciwnym
        /// przypadku drzewo rozpinające mst jest takiego samego typu jak graf g.<para/>
        /// Dla grafu skierowanego metoda zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Wyznaczone drzewo reprezentowane jest jako graf bez cykli, to umożliwia jednolitą obsługę sytuacji
        /// gdy analizowany graf jest niespójny, wyznaczany jest wówczas las rozpinający.
        /// </remarks>
        /// <seealso cref="MSTGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Graph mst) Kruskal(this Graph g)
        {
            if (g.Directed)
            {
                throw new ArgumentException("Directed graphs are not allowed");
            }

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

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

            while (tree.EdgesCount < g.VerticesCount - 1 && !edgesMinPriorityQueue.Empty)
            {
                var edge = edgesMinPriorityQueue.Get();
                if (!unionFind.Union(edge.From, edge.To))
                {
                    continue;
                }
                tree.AddEdge(edge);
                weight += edge.Weight;
            }
            return(weight, tree);
        }
        /// <summary>
        /// Wyznacza minimalne drzewo rozpinające grafu algorytmem 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);
        }
Exemple #4
0
        public static Graph Kruskal(Graph graph)
        {
            var edges = getGraphEdgesMin(graph);

            UnionFind uf       = new UnionFind(graph.VerticesCount);
            var       retGraph = new AdjacencyMatrixGraph(false, graph.VerticesCount);

            foreach (Edge e in edges.ToArray())
            {
                if (uf.Find(e.From) != uf.Find(e.To))
                {
                    retGraph.AddEdge(e);
                    uf.Union(e.From, e.To);
                }
            }

            return(retGraph);
        }
Exemple #5
0
        /// <summary>
        /// Znajduje rozwiązanie przybliżone problemu komiwojażera algorytmem zachłannym "kruskalopodobnym"
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <returns>Krotka (weight, cycle) składająca się z długości (sumy wag krawędzi) znalezionego cyklu i tablicy krawędzi tworzących ten cykl)</returns>
        /// <remarks>
        /// Elementy (krawędzie) umieszczone są w tablicy cycle w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<para/>
        /// Jeśli algorytm "kruskalopodobny" nie znajdzie w badanym grafie cyklu Hamiltona (co oczywiście nie znaczy, że taki cykl nie istnieje) to metoda zwraca krotkę (NaN,null).<para/>
        /// Metodę można stosować dla grafów skierowanych i nieskierowanych.<para/>
        /// Metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi.
        /// </remarks>
        /// <seealso cref="AproxTSPGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Edge[] cycle) KruskalTSP(this Graph g)
        {
            if (g.VerticesCount <= (g.Directed ? 1 : 2))
            {
                return(double.NaN, null);
            }

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

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

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

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

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

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

            for (var i = 0; i < g.VerticesCount;)
            {
                foreach (var edge in graph.OutEdges(vert))
                {
                    if (edge.To == prevVert)
                    {
                        continue;
                    }
                    cycle[i++] = edge;
                    weight    += edge.Weight;
                    prevVert   = vert;
                    vert       = edge.To;
                    break;
                }
            }
            return(weight, cycle);
        }
Exemple #6
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
Exemple #7
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