Exemplo n.º 1
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);
        }
Exemplo n.º 2
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