lemon/insertion_tsp.h
author Peter Kovacs <kpeter@inf.elte.hu>
Sun, 09 Jan 2011 15:06:55 +0100
changeset 1204 dff32ce3db71
parent 1202 ef200e268af2
child 1205 d3dcc49e6403
permissions -rw-r--r--
Make InsertionTsp much faster and improve docs (#386)
     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-2010
     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_INSERTION_TSP_H
    20 #define LEMON_INSERTION_TSP_H
    21 
    22 /// \ingroup tsp
    23 /// \file
    24 /// \brief Insertion algorithm for symmetric TSP
    25 
    26 #include <vector>
    27 #include <functional>
    28 #include <lemon/full_graph.h>
    29 #include <lemon/maps.h>
    30 #include <lemon/random.h>
    31 
    32 namespace lemon {
    33 
    34   /// \ingroup tsp
    35   ///
    36   /// \brief Insertion algorithm for symmetric TSP.
    37   ///
    38   /// InsertionTsp implements the insertion heuristic for solving
    39   /// symmetric \ref tsp "TSP".
    40   ///
    41   /// This is a fast and effective tour construction method that has
    42   /// many variants.
    43   /// It starts with a subtour containing a few nodes of the graph and it
    44   /// iteratively inserts the other nodes into this subtour according to a
    45   /// certain node selection rule.
    46   ///
    47   /// This method is among the fastest TSP algorithms, and it typically
    48   /// provides quite good solutions (usually much better than
    49   /// \ref NearestNeighborTsp and \ref GreedyTsp).
    50   ///
    51   /// InsertionTsp implements four different node selection rules,
    52   /// from which the most effective one (\e farthest \e node \e selection)
    53   /// is used by default.
    54   /// With this choice, the algorithm runs in O(n<sup>2</sup>) time.
    55   /// For more information, see \ref SelectionRule.
    56   ///
    57   /// \tparam CM Type of the cost map.
    58   template <typename CM>
    59   class InsertionTsp
    60   {
    61     public:
    62 
    63       /// Type of the cost map
    64       typedef CM CostMap;
    65       /// Type of the edge costs
    66       typedef typename CM::Value Cost;
    67 
    68     private:
    69 
    70       GRAPH_TYPEDEFS(FullGraph);
    71 
    72       const FullGraph &_gr;
    73       const CostMap &_cost;
    74       std::vector<Node> _notused;
    75       std::vector<Node> _tour;
    76       Cost _sum;
    77 
    78     public:
    79 
    80       /// \brief Constants for specifying the node selection rule.
    81       ///
    82       /// Enum type containing constants for specifying the node selection
    83       /// rule for the \ref run() function.
    84       ///
    85       /// During the algorithm, nodes are selected for addition to the current
    86       /// subtour according to the applied rule.
    87       /// The FARTHEST method is one of the fastest selection rules, and
    88       /// it is typically the most effective, thus it is the default
    89       /// option. The RANDOM rule usually gives slightly worse results,
    90       /// but it is more robust.
    91       ///
    92       /// The desired selection rule can be specified as a parameter of the
    93       /// \ref run() function.
    94       enum SelectionRule {
    95 
    96         /// An unvisited node having minimum distance from the current
    97         /// subtour is selected at each step.
    98         /// The algorithm runs in O(n<sup>2</sup>) time using this
    99         /// selection rule.
   100         NEAREST,
   101 
   102         /// An unvisited node having maximum distance from the current
   103         /// subtour is selected at each step.
   104         /// The algorithm runs in O(n<sup>2</sup>) time using this
   105         /// selection rule.
   106         FARTHEST,
   107 
   108         /// An unvisited node whose insertion results in the least
   109         /// increase of the subtour's total cost is selected at each step.
   110         /// The algorithm runs in O(n<sup>3</sup>) time using this
   111         /// selection rule, but in most cases, it is almost as fast as
   112         /// with other rules.
   113         CHEAPEST,
   114 
   115         /// An unvisited node is selected randomly without any evaluation
   116         /// at each step.
   117         /// The global \ref rnd "random number generator instance" is used.
   118         /// You can seed it before executing the algorithm, if you
   119         /// would like to.
   120         /// The algorithm runs in O(n<sup>2</sup>) time using this
   121         /// selection rule.
   122         RANDOM
   123       };
   124 
   125     public:
   126 
   127       /// \brief Constructor
   128       ///
   129       /// Constructor.
   130       /// \param gr The \ref FullGraph "full graph" the algorithm runs on.
   131       /// \param cost The cost map.
   132       InsertionTsp(const FullGraph &gr, const CostMap &cost)
   133         : _gr(gr), _cost(cost) {}
   134 
   135       /// \name Execution Control
   136       /// @{
   137 
   138       /// \brief Runs the algorithm.
   139       ///
   140       /// This function runs the algorithm.
   141       ///
   142       /// \param rule The node selection rule. For more information, see
   143       /// \ref SelectionRule.
   144       ///
   145       /// \return The total cost of the found tour.
   146       Cost run(SelectionRule rule = FARTHEST) {
   147         _tour.clear();
   148 
   149         if (_gr.nodeNum() == 0) return _sum = 0;
   150         else if (_gr.nodeNum() == 1) {
   151           _tour.push_back(_gr(0));
   152           return _sum = 0;
   153         }
   154 
   155         switch (rule) {
   156           case NEAREST:
   157             init(true);
   158             start<ComparingSelection<std::less<Cost> >,
   159                   DefaultInsertion>();
   160             break;
   161           case FARTHEST:
   162             init(false);
   163             start<ComparingSelection<std::greater<Cost> >,
   164                   DefaultInsertion>();
   165             break;
   166           case CHEAPEST:
   167             init(true);
   168             start<CheapestSelection, CheapestInsertion>();
   169             break;
   170           case RANDOM:
   171             init(true);
   172             start<RandomSelection, DefaultInsertion>();
   173             break;
   174         }
   175         return _sum;
   176       }
   177 
   178       /// @}
   179 
   180       /// \name Query Functions
   181       /// @{
   182 
   183       /// \brief The total cost of the found tour.
   184       ///
   185       /// This function returns the total cost of the found tour.
   186       ///
   187       /// \pre run() must be called before using this function.
   188       Cost tourCost() const {
   189         return _sum;
   190       }
   191 
   192       /// \brief Returns a const reference to the node sequence of the
   193       /// found tour.
   194       ///
   195       /// This function returns a const reference to a vector
   196       /// that stores the node sequence of the found tour.
   197       ///
   198       /// \pre run() must be called before using this function.
   199       const std::vector<Node>& tourNodes() const {
   200         return _tour;
   201       }
   202 
   203       /// \brief Gives back the node sequence of the found tour.
   204       ///
   205       /// This function copies the node sequence of the found tour into
   206       /// the given standard container.
   207       ///
   208       /// \pre run() must be called before using this function.
   209       template <typename Container>
   210       void tourNodes(Container &container) const {
   211         container.assign(_tour.begin(), _tour.end());
   212       }
   213 
   214       /// \brief Gives back the found tour as a path.
   215       ///
   216       /// This function copies the found tour as a list of arcs/edges into
   217       /// the given \ref concept::Path "path structure".
   218       ///
   219       /// \pre run() must be called before using this function.
   220       template <typename Path>
   221       void tour(Path &path) const {
   222         path.clear();
   223         for (int i = 0; i < int(_tour.size()) - 1; ++i) {
   224           path.addBack(_gr.arc(_tour[i], _tour[i+1]));
   225         }
   226         if (int(_tour.size()) >= 2) {
   227           path.addBack(_gr.arc(_tour.back(), _tour.front()));
   228         }
   229       }
   230 
   231       /// @}
   232 
   233     private:
   234 
   235       // Initializes the algorithm
   236       void init(bool min) {
   237         Edge min_edge = min ? mapMin(_gr, _cost) : mapMax(_gr, _cost);
   238 
   239         _tour.clear();
   240         _tour.push_back(_gr.u(min_edge));
   241         _tour.push_back(_gr.v(min_edge));
   242 
   243         _notused.clear();
   244         for (NodeIt n(_gr); n!=INVALID; ++n) {
   245           if (n != _gr.u(min_edge) && n != _gr.v(min_edge)) {
   246             _notused.push_back(n);
   247           }
   248         }
   249 
   250         _sum = _cost[min_edge] * 2;
   251       }
   252 
   253       // Executes the algorithm
   254       template <class SelectionFunctor, class InsertionFunctor>
   255       void start() {
   256         SelectionFunctor selectNode(_gr, _cost, _tour, _notused);
   257         InsertionFunctor insertNode(_gr, _cost, _tour, _sum);
   258 
   259         for (int i=0; i<_gr.nodeNum()-2; ++i) {
   260           insertNode.insert(selectNode.select());
   261         }
   262 
   263         _sum = _cost[_gr.edge(_tour.back(), _tour.front())];
   264         for (int i = 0; i < int(_tour.size())-1; ++i) {
   265           _sum += _cost[_gr.edge(_tour[i], _tour[i+1])];
   266         }
   267       }
   268 
   269 
   270       // Implementation of the nearest and farthest selection rule
   271       template <typename Comparator>
   272       class ComparingSelection {
   273         public:
   274           ComparingSelection(const FullGraph &gr, const CostMap &cost,
   275                   std::vector<Node> &tour, std::vector<Node> &notused)
   276             : _gr(gr), _cost(cost), _tour(tour), _notused(notused),
   277               _dist(gr, 0), _compare()
   278           {
   279             // Compute initial distances for the unused nodes
   280             for (unsigned int i=0; i<_notused.size(); ++i) {
   281               Node u = _notused[i];
   282               Cost min_dist = _cost[_gr.edge(u, _tour[0])];
   283               for (unsigned int j=1; j<_tour.size(); ++j) {
   284                 Cost curr = _cost[_gr.edge(u, _tour[j])];
   285                 if (curr < min_dist) {
   286                   min_dist = curr;
   287                 }
   288               }
   289               _dist[u] = min_dist;
   290             }
   291           }
   292 
   293           Node select() {
   294 
   295             // Select an used node with minimum distance
   296             Cost ins_dist = 0;
   297             int ins_node = -1;
   298             for (unsigned int i=0; i<_notused.size(); ++i) {
   299               Cost curr = _dist[_notused[i]];
   300               if (_compare(curr, ins_dist) || ins_node == -1) {
   301                 ins_dist = curr;
   302                 ins_node = i;
   303               }
   304             }
   305 
   306             // Remove the selected node from the unused vector
   307             Node sn = _notused[ins_node];
   308             _notused[ins_node] = _notused.back();
   309             _notused.pop_back();
   310 
   311             // Update the distances of the remaining nodes
   312             for (unsigned int i=0; i<_notused.size(); ++i) {
   313               Node u = _notused[i];
   314               Cost nc = _cost[_gr.edge(sn, u)];
   315               if (nc < _dist[u]) {
   316                 _dist[u] = nc;
   317               }
   318             }
   319 
   320             return sn;
   321           }
   322 
   323         private:
   324           const FullGraph &_gr;
   325           const CostMap &_cost;
   326           std::vector<Node> &_tour;
   327           std::vector<Node> &_notused;
   328           FullGraph::NodeMap<Cost> _dist;
   329           Comparator _compare;
   330       };
   331 
   332       // Implementation of the cheapest selection rule
   333       class CheapestSelection {
   334         private:
   335           Cost costDiff(Node u, Node v, Node w) const {
   336             return
   337               _cost[_gr.edge(u, w)] +
   338               _cost[_gr.edge(v, w)] -
   339               _cost[_gr.edge(u, v)];
   340           }
   341 
   342         public:
   343           CheapestSelection(const FullGraph &gr, const CostMap &cost,
   344                             std::vector<Node> &tour, std::vector<Node> &notused)
   345             : _gr(gr), _cost(cost), _tour(tour), _notused(notused),
   346               _ins_cost(gr, 0), _ins_pos(gr, -1)
   347           {
   348             // Compute insertion cost and position for the unused nodes
   349             for (unsigned int i=0; i<_notused.size(); ++i) {
   350               Node u = _notused[i];
   351               Cost min_cost = costDiff(_tour.back(), _tour.front(), u);
   352               int min_pos = 0;              
   353               for (unsigned int j=1; j<_tour.size(); ++j) {
   354                 Cost curr_cost = costDiff(_tour[j-1], _tour[j], u);
   355                 if (curr_cost < min_cost) {
   356                   min_cost = curr_cost;
   357                   min_pos = j;
   358                 }
   359               }
   360               _ins_cost[u] = min_cost;
   361               _ins_pos[u] = min_pos;
   362             }
   363           }
   364 
   365           Cost select() {
   366 
   367             // Select an used node with minimum insertion cost
   368             Cost min_cost = 0;
   369             int min_node = -1;
   370             for (unsigned int i=0; i<_notused.size(); ++i) {
   371               Cost curr_cost = _ins_cost[_notused[i]];
   372               if (curr_cost < min_cost || min_node == -1) {
   373                 min_cost = curr_cost;
   374                 min_node = i;
   375               }
   376             }
   377 
   378             // Remove the selected node from the unused vector
   379             Node sn = _notused[min_node];
   380             _notused[min_node] = _notused.back();
   381             _notused.pop_back();
   382             
   383             // Insert the selected node into the tour
   384             const int ipos = _ins_pos[sn];
   385             _tour.insert(_tour.begin() + ipos, sn);
   386 
   387             // Update the insertion cost and position of the remaining nodes
   388             for (unsigned int i=0; i<_notused.size(); ++i) {
   389               Node u = _notused[i];
   390               Cost curr_cost = _ins_cost[u];
   391               int curr_pos = _ins_pos[u];
   392 
   393               int ipos_prev = ipos == 0 ? _tour.size()-1 : ipos-1;
   394               int ipos_next = ipos == int(_tour.size())-1 ? 0 : ipos+1;
   395               Cost nc1 = costDiff(_tour[ipos_prev], _tour[ipos], u);
   396               Cost nc2 = costDiff(_tour[ipos], _tour[ipos_next], u);
   397               
   398               if (nc1 <= curr_cost || nc2 <= curr_cost) {
   399                 // A new position is better than the old one
   400                 if (nc1 <= nc2) {
   401                   curr_cost = nc1;
   402                   curr_pos = ipos;
   403                 } else {
   404                   curr_cost = nc2;
   405                   curr_pos = ipos_next;
   406                 }
   407               }
   408               else {
   409                 if (curr_pos == ipos) {
   410                   // The minimum should be found again
   411                   curr_cost = costDiff(_tour.back(), _tour.front(), u);
   412                   curr_pos = 0;              
   413                   for (unsigned int j=1; j<_tour.size(); ++j) {
   414                     Cost tmp_cost = costDiff(_tour[j-1], _tour[j], u);
   415                     if (tmp_cost < curr_cost) {
   416                       curr_cost = tmp_cost;
   417                       curr_pos = j;
   418                     }
   419                   }
   420                 }
   421                 else if (curr_pos > ipos) {
   422                   ++curr_pos;
   423                 }
   424               }
   425               
   426               _ins_cost[u] = curr_cost;
   427               _ins_pos[u] = curr_pos;
   428             }
   429 
   430             return min_cost;
   431           }
   432 
   433         private:
   434           const FullGraph &_gr;
   435           const CostMap &_cost;
   436           std::vector<Node> &_tour;
   437           std::vector<Node> &_notused;
   438           FullGraph::NodeMap<Cost> _ins_cost;
   439           FullGraph::NodeMap<int> _ins_pos;
   440       };
   441 
   442       // Implementation of the random selection rule
   443       class RandomSelection {
   444         public:
   445           RandomSelection(const FullGraph &, const CostMap &,
   446                           std::vector<Node> &, std::vector<Node> &notused)
   447             : _notused(notused) {}
   448 
   449           Node select() const {
   450             const int index = rnd[_notused.size()];
   451             Node n = _notused[index];
   452             _notused[index] = _notused.back();
   453             _notused.pop_back();
   454             return n;
   455           }
   456 
   457         private:
   458           std::vector<Node> &_notused;
   459       };
   460 
   461 
   462       // Implementation of the default insertion method
   463       class DefaultInsertion {
   464         private:
   465           Cost costDiff(Node u, Node v, Node w) const {
   466             return
   467               _cost[_gr.edge(u, w)] +
   468               _cost[_gr.edge(v, w)] -
   469               _cost[_gr.edge(u, v)];
   470           }
   471 
   472         public:
   473           DefaultInsertion(const FullGraph &gr, const CostMap &cost,
   474                            std::vector<Node> &tour, Cost &total_cost) :
   475             _gr(gr), _cost(cost), _tour(tour), _total(total_cost) {}
   476 
   477           void insert(Node n) const {
   478             int min = 0;
   479             Cost min_val =
   480               costDiff(_tour.front(), _tour.back(), n);
   481 
   482             for (unsigned int i=1; i<_tour.size(); ++i) {
   483               Cost tmp = costDiff(_tour[i-1], _tour[i], n);
   484               if (tmp < min_val) {
   485                 min = i;
   486                 min_val = tmp;
   487               }
   488             }
   489 
   490             _tour.insert(_tour.begin()+min, n);
   491             _total += min_val;
   492           }
   493 
   494         private:
   495           const FullGraph &_gr;
   496           const CostMap &_cost;
   497           std::vector<Node> &_tour;
   498           Cost &_total;
   499       };
   500 
   501       // Implementation of a special insertion method for the cheapest
   502       // selection rule
   503       class CheapestInsertion {
   504         TEMPLATE_GRAPH_TYPEDEFS(FullGraph);
   505         public:
   506           CheapestInsertion(const FullGraph &, const CostMap &,
   507                             std::vector<Node> &, Cost &total_cost) :
   508             _total(total_cost) {}
   509 
   510           void insert(Cost diff) const {
   511             _total += diff;
   512           }
   513 
   514         private:
   515           Cost &_total;
   516       };
   517 
   518   };
   519 
   520 }; // namespace lemon
   521 
   522 #endif