lemon/christofides_tsp.h
author Balazs Dezso <deba@google.com>
Fri, 22 Jan 2021 10:55:32 +0100
changeset 1208 c6aa2cc1af04
parent 1074 97d978243703
permissions -rw-r--r--
Factor out recursion from weighted matching algorithms (#638)
     1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library.
     4  *
     5  * Copyright (C) 2003-2013
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     9  * Permission to use, modify and distribute this software is granted
    10  * provided that this copyright notice appears in all copies. For
    11  * precise terms see the accompanying LICENSE file.
    12  *
    13  * This software is provided "AS IS" with no warranty of any kind,
    14  * express or implied, and with no claim as to its suitability for any
    15  * purpose.
    16  *
    17  */
    18 
    19 #ifndef LEMON_CHRISTOFIDES_TSP_H
    20 #define LEMON_CHRISTOFIDES_TSP_H
    21 
    22 /// \ingroup tsp
    23 /// \file
    24 /// \brief Christofides algorithm for symmetric TSP
    25 
    26 #include <lemon/full_graph.h>
    27 #include <lemon/smart_graph.h>
    28 #include <lemon/kruskal.h>
    29 #include <lemon/matching.h>
    30 #include <lemon/euler.h>
    31 
    32 namespace lemon {
    33 
    34   /// \ingroup tsp
    35   ///
    36   /// \brief Christofides algorithm for symmetric TSP.
    37   ///
    38   /// ChristofidesTsp implements Christofides' heuristic for solving
    39   /// symmetric \ref tsp "TSP".
    40   ///
    41   /// This a well-known approximation method for the TSP problem with
    42   /// metric cost function.
    43   /// It has a guaranteed approximation factor of 3/2 (i.e. it finds a tour
    44   /// whose total cost is at most 3/2 of the optimum), but it usually
    45   /// provides better solutions in practice.
    46   /// This implementation runs in O(n<sup>3</sup>log(n)) time.
    47   ///
    48   /// The algorithm starts with a \ref spantree "minimum cost spanning tree" and
    49   /// finds a \ref MaxWeightedPerfectMatching "minimum cost perfect matching"
    50   /// in the subgraph induced by the nodes that have odd degree in the
    51   /// spanning tree.
    52   /// Finally, it constructs the tour from the \ref EulerIt "Euler traversal"
    53   /// of the union of the spanning tree and the matching.
    54   /// During this last step, the algorithm simply skips the visited nodes
    55   /// (i.e. creates shortcuts) assuming that the triangle inequality holds
    56   /// for the cost function.
    57   ///
    58   /// \tparam CM Type of the cost map.
    59   ///
    60   /// \warning CM::Value must be a signed number type.
    61   template <typename CM>
    62   class ChristofidesTsp
    63   {
    64     public:
    65 
    66       /// Type of the cost map
    67       typedef CM CostMap;
    68       /// Type of the edge costs
    69       typedef typename CM::Value Cost;
    70 
    71     private:
    72 
    73       GRAPH_TYPEDEFS(FullGraph);
    74 
    75       const FullGraph &_gr;
    76       const CostMap &_cost;
    77       std::vector<Node> _path;
    78       Cost _sum;
    79 
    80     public:
    81 
    82       /// \brief Constructor
    83       ///
    84       /// Constructor.
    85       /// \param gr The \ref FullGraph "full graph" the algorithm runs on.
    86       /// \param cost The cost map.
    87       ChristofidesTsp(const FullGraph &gr, const CostMap &cost)
    88         : _gr(gr), _cost(cost) {}
    89 
    90       /// \name Execution Control
    91       /// @{
    92 
    93       /// \brief Runs the algorithm.
    94       ///
    95       /// This function runs the algorithm.
    96       ///
    97       /// \return The total cost of the found tour.
    98       Cost run() {
    99         _path.clear();
   100 
   101         if (_gr.nodeNum() == 0) return _sum = 0;
   102         else if (_gr.nodeNum() == 1) {
   103           _path.push_back(_gr(0));
   104           return _sum = 0;
   105         }
   106         else if (_gr.nodeNum() == 2) {
   107           _path.push_back(_gr(0));
   108           _path.push_back(_gr(1));
   109           return _sum = 2 * _cost[_gr.edge(_gr(0), _gr(1))];
   110         }
   111 
   112         // Compute min. cost spanning tree
   113         std::vector<Edge> tree;
   114         kruskal(_gr, _cost, std::back_inserter(tree));
   115 
   116         FullGraph::NodeMap<int> deg(_gr, 0);
   117         for (int i = 0; i != int(tree.size()); ++i) {
   118           Edge e = tree[i];
   119           ++deg[_gr.u(e)];
   120           ++deg[_gr.v(e)];
   121         }
   122 
   123         // Copy the induced subgraph of odd nodes
   124         std::vector<Node> odd_nodes;
   125         for (NodeIt u(_gr); u != INVALID; ++u) {
   126           if (deg[u] % 2 == 1) odd_nodes.push_back(u);
   127         }
   128 
   129         SmartGraph sgr;
   130         SmartGraph::EdgeMap<Cost> scost(sgr);
   131         for (int i = 0; i != int(odd_nodes.size()); ++i) {
   132           sgr.addNode();
   133         }
   134         for (int i = 0; i != int(odd_nodes.size()); ++i) {
   135           for (int j = 0; j != int(odd_nodes.size()); ++j) {
   136             if (j == i) continue;
   137             SmartGraph::Edge e =
   138               sgr.addEdge(sgr.nodeFromId(i), sgr.nodeFromId(j));
   139             scost[e] = -_cost[_gr.edge(odd_nodes[i], odd_nodes[j])];
   140           }
   141         }
   142 
   143         // Compute min. cost perfect matching
   144         MaxWeightedPerfectMatching<SmartGraph, SmartGraph::EdgeMap<Cost> >
   145           mwpm(sgr, scost);
   146         mwpm.run();
   147 
   148         for (SmartGraph::EdgeIt e(sgr); e != INVALID; ++e) {
   149           if (mwpm.matching(e)) {
   150             tree.push_back( _gr.edge(odd_nodes[sgr.id(sgr.u(e))],
   151                                      odd_nodes[sgr.id(sgr.v(e))]) );
   152           }
   153         }
   154 
   155         // Join the spanning tree and the matching
   156         sgr.clear();
   157         for (int i = 0; i != _gr.nodeNum(); ++i) {
   158           sgr.addNode();
   159         }
   160         for (int i = 0; i != int(tree.size()); ++i) {
   161           int ui = _gr.id(_gr.u(tree[i])),
   162               vi = _gr.id(_gr.v(tree[i]));
   163           sgr.addEdge(sgr.nodeFromId(ui), sgr.nodeFromId(vi));
   164         }
   165 
   166         // Compute the tour from the Euler traversal
   167         SmartGraph::NodeMap<bool> visited(sgr, false);
   168         for (EulerIt<SmartGraph> e(sgr); e != INVALID; ++e) {
   169           SmartGraph::Node n = sgr.target(e);
   170           if (!visited[n]) {
   171             _path.push_back(_gr(sgr.id(n)));
   172             visited[n] = true;
   173           }
   174         }
   175 
   176         _sum = _cost[_gr.edge(_path.back(), _path.front())];
   177         for (int i = 0; i < int(_path.size())-1; ++i) {
   178           _sum += _cost[_gr.edge(_path[i], _path[i+1])];
   179         }
   180 
   181         return _sum;
   182       }
   183 
   184       /// @}
   185 
   186       /// \name Query Functions
   187       /// @{
   188 
   189       /// \brief The total cost of the found tour.
   190       ///
   191       /// This function returns the total cost of the found tour.
   192       ///
   193       /// \pre run() must be called before using this function.
   194       Cost tourCost() const {
   195         return _sum;
   196       }
   197 
   198       /// \brief Returns a const reference to the node sequence of the
   199       /// found tour.
   200       ///
   201       /// This function returns a const reference to a vector
   202       /// that stores the node sequence of the found tour.
   203       ///
   204       /// \pre run() must be called before using this function.
   205       const std::vector<Node>& tourNodes() const {
   206         return _path;
   207       }
   208 
   209       /// \brief Gives back the node sequence of the found tour.
   210       ///
   211       /// This function copies the node sequence of the found tour into
   212       /// an STL container through the given output iterator. The
   213       /// <tt>value_type</tt> of the container must be <tt>FullGraph::Node</tt>.
   214       /// For example,
   215       /// \code
   216       /// std::vector<FullGraph::Node> nodes(countNodes(graph));
   217       /// tsp.tourNodes(nodes.begin());
   218       /// \endcode
   219       /// or
   220       /// \code
   221       /// std::list<FullGraph::Node> nodes;
   222       /// tsp.tourNodes(std::back_inserter(nodes));
   223       /// \endcode
   224       ///
   225       /// \pre run() must be called before using this function.
   226       template <typename Iterator>
   227       void tourNodes(Iterator out) const {
   228         std::copy(_path.begin(), _path.end(), out);
   229       }
   230 
   231       /// \brief Gives back the found tour as a path.
   232       ///
   233       /// This function copies the found tour as a list of arcs/edges into
   234       /// the given \ref lemon::concepts::Path "path structure".
   235       ///
   236       /// \pre run() must be called before using this function.
   237       template <typename Path>
   238       void tour(Path &path) const {
   239         path.clear();
   240         for (int i = 0; i < int(_path.size()) - 1; ++i) {
   241           path.addBack(_gr.arc(_path[i], _path[i+1]));
   242         }
   243         if (int(_path.size()) >= 2) {
   244           path.addBack(_gr.arc(_path.back(), _path.front()));
   245         }
   246       }
   247 
   248       /// @}
   249 
   250   };
   251 
   252 }; // namespace lemon
   253 
   254 #endif