Ejemplo n.º 1
0
        /// <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="version">Wersja algorytmu (prosta, Christofidesa lub zmodyfikowana Christofidesa)</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>
        /// Parametr version opisany jest w wyliczeniu <see cref="TSPTreeBasedVersion"/>.<para/>
        /// Elementy (krawędzie) umieszczone są w tablicy cycle w kolejności swojego następstwa w znalezionym cyklu Hamiltona.<para/>
        /// 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 krotkę (NaN,null).<para/>
        /// Metodę można stosować dla grafów nieskierowanych.<para/>
        /// Zastosowana do grafu skierowanego zgłasza wyjątek <see cref="ArgumentException"/>>.<para/>
        /// Dla wartości parametru <see cref="TSPTreeBasedVersion.Simple"/>) metodę można stosować dla dla grafów z dowolnymi (również ujemnymi) wagami krawędzi,
        /// dla innych wartości parametru version wagi krawędzi powinny być z przedziału &lt;float.MinValue,float.MaxValue&gt;.<para/>
        /// Dla grafu nieskierowanego spełniajacego nierówność trójkąta metoda realizuje 1.5-aproksymacyjny algorytm Christofidesa
        /// (gdy parametr version ma wartość <see cref="TSPTreeBasedVersion.Christofides"/>) lub algorytm 2-aproksymacyjny (dla innych wartości parametru version).
        /// </remarks>
        /// <seealso cref="AproxTSPGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Edge[] cycle) TreeBasedTSP(this Graph g, TSPTreeBasedVersion version = TSPTreeBasedVersion.Simple)
        {
            if (g.Directed)
            {
                throw new ArgumentException("Directed graphs are not allowed");
            }
            if (g.VerticesCount <= 2)
            {
                return(double.NaN, null);
            }
            var tree = g.Prim().mst;

            if (tree.EdgesCount != g.VerticesCount - 1)
            {
                return(double.NaN, null);
            }
            if (version != TSPTreeBasedVersion.Simple)
            {
                var oddDegreeVertices = new bool[g.VerticesCount];
                if (version == TSPTreeBasedVersion.Christofides)
                {
                    for (var i = 0; i < g.VerticesCount; i++)
                    {
                        oddDegreeVertices[i] = (tree.OutDegree(i) % 2 == 1);
                    }
                }
                else
                {
                    var count = 0;
                    for (var i = 0; i < g.VerticesCount; i++)
                    {
                        if (tree.OutDegree(i) != 1)
                        {
                            continue;
                        }
                        oddDegreeVertices[i] = true;
                        count++;
                    }
                    if (count % 2 == 1)
                    {
                        var i = 0;
                        for (; tree.OutDegree(i) % 2 == 0 || tree.OutDegree(i) == 1; i++)
                        {
                            ;
                        }
                        oddDegreeVertices[i] = true;
                    }
                }
                foreach (var edge in g.PerfectMatching(oddDegreeVertices))
                {
                    tree.AddEdge(edge);
                }
            }
            var weight       = 0.0;
            var lastVert     = 0;
            var cycleCounter = 0;
            var cycle        = new Edge[g.VerticesCount];

            bool PreVisitVertex(int vert)
            {
                if (vert == 0)
                {
                    return(true);
                }
                var w = g.GetEdgeWeight(lastVert, vert);

                cycle[cycleCounter++] = new Edge(lastVert, vert, w);
                weight  += w;
                lastVert = vert;
                return(true);
            }

            tree.GeneralSearchAll <EdgesStack>(PreVisitVertex, null, null, out _);
            var edgeWeight = g.GetEdgeWeight(lastVert, 0);

            cycle[g.VerticesCount - 1] = new Edge(lastVert, 0, edgeWeight);
            weight += edgeWeight;
            return(!weight.IsNaN() ? (weight, cycle) : (double.NaN, null));
        }