[Lemon-commits] Peter Kovacs: Make InsertionTsp much faster and ...

Lemon HG hg at lemon.cs.elte.hu
Fri Mar 1 18:03:16 CET 2013


details:   http://lemon.cs.elte.hu/hg/lemon/rev/dff32ce3db71
changeset: 1204:dff32ce3db71
user:      Peter Kovacs <kpeter [at] inf.elte.hu>
date:      Sun Jan 09 15:06:55 2011 +0100
description:
	Make InsertionTsp much faster and improve docs (#386)

diffstat:

 doc/groups.dox               |   10 +
 lemon/christofides_tsp.h     |    5 +-
 lemon/greedy_tsp.h           |    7 +-
 lemon/insertion_tsp.h        |  312 ++++++++++++++++++++++++------------------
 lemon/nearest_neighbor_tsp.h |    5 +-
 lemon/opt2_tsp.h             |    6 +-
 6 files changed, 200 insertions(+), 145 deletions(-)

diffs (truncated from 583 to 300 lines):

diff --git a/doc/groups.dox b/doc/groups.dox
--- a/doc/groups.dox
+++ b/doc/groups.dox
@@ -572,6 +572,16 @@
  - \ref ChristofidesTsp Christofides algorithm
  - \ref Opt2Tsp 2-opt algorithm
 
+\ref NearestNeighborTsp, \ref GreedyTsp, and \ref InsertionTsp are the fastest
+solution methods. Furthermore, \ref InsertionTsp is usually quite effective.
+
+\ref ChristofidesTsp is somewhat slower, but it has the best guaranteed
+approximation factor: 3/2.
+
+\ref Opt2Tsp usually provides the best results in practice, but
+it is the slowest method. It can also be used to improve given tours,
+for example, the results of other algorithms.
+
 \image html tsp.png
 \image latex tsp.eps "Traveling salesman problem" width=\textwidth
 */
diff --git a/lemon/christofides_tsp.h b/lemon/christofides_tsp.h
--- a/lemon/christofides_tsp.h
+++ b/lemon/christofides_tsp.h
@@ -40,8 +40,9 @@
   ///
   /// This a well-known approximation method for the TSP problem with
   /// metric cost function.
-  /// It yields a tour whose total cost is at most 3/2 of the optimum,
-  /// but it is usually much better.
+  /// It has a guaranteed approximation factor of 3/2 (i.e. it finds a tour
+  /// whose total cost is at most 3/2 of the optimum), but it usually
+  /// provides better solutions in practice.
   /// This implementation runs in O(n<sup>3</sup>log(n)) time.
   ///
   /// The algorithm starts with a \ref spantree "minimum cost spanning tree" and
diff --git a/lemon/greedy_tsp.h b/lemon/greedy_tsp.h
--- a/lemon/greedy_tsp.h
+++ b/lemon/greedy_tsp.h
@@ -43,9 +43,10 @@
   /// as long as it does not create a cycle of less than n edges and it does
   /// not increase the degree of any node above two.
   ///
-  /// This method runs in O(n<sup>2</sup>log(n)) time.
-  /// It quickly finds a short tour for most TSP instances, but in special
-  /// cases, it could yield a really bad (or even the worst) solution.
+  /// This method runs in O(n<sup>2</sup>) time.
+  /// It quickly finds a relatively short tour for most TSP instances,
+  /// but it could also yield a really bad (or even the worst) solution
+  /// in special cases.
   ///
   /// \tparam CM Type of the cost map.
   template <typename CM>
diff --git a/lemon/insertion_tsp.h b/lemon/insertion_tsp.h
--- a/lemon/insertion_tsp.h
+++ b/lemon/insertion_tsp.h
@@ -24,6 +24,7 @@
 /// \brief Insertion algorithm for symmetric TSP
 
 #include <vector>
+#include <functional>
 #include <lemon/full_graph.h>
 #include <lemon/maps.h>
 #include <lemon/random.h>
@@ -37,13 +38,20 @@
   /// InsertionTsp implements the insertion heuristic for solving
   /// symmetric \ref tsp "TSP".
   ///
-  /// This is a basic TSP heuristic that has many variants.
+  /// This is a fast and effective tour construction method that has
+  /// many variants.
   /// It starts with a subtour containing a few nodes of the graph and it
   /// iteratively inserts the other nodes into this subtour according to a
   /// certain node selection rule.
   ///
-  /// This implementation provides four different node selection rules,
-  /// from which the most powerful one is used by default.
+  /// This method is among the fastest TSP algorithms, and it typically
+  /// provides quite good solutions (usually much better than
+  /// \ref NearestNeighborTsp and \ref GreedyTsp).
+  ///
+  /// InsertionTsp implements four different node selection rules,
+  /// from which the most effective one (\e farthest \e node \e selection)
+  /// is used by default.
+  /// With this choice, the algorithm runs in O(n<sup>2</sup>) time.
   /// For more information, see \ref SelectionRule.
   ///
   /// \tparam CM Type of the cost map.
@@ -64,7 +72,7 @@
       const FullGraph &_gr;
       const CostMap &_cost;
       std::vector<Node> _notused;
-      std::vector<Node> _path;
+      std::vector<Node> _tour;
       Cost _sum;
 
     public:
@@ -76,9 +84,10 @@
       ///
       /// During the algorithm, nodes are selected for addition to the current
       /// subtour according to the applied rule.
-      /// In general, the FARTHEST method yields the best tours, thus it is the
-      /// default option. The RANDOM rule usually gives somewhat worse results,
-      /// but it is much faster than the others and it is the most robust.
+      /// The FARTHEST method is one of the fastest selection rules, and
+      /// it is typically the most effective, thus it is the default
+      /// option. The RANDOM rule usually gives slightly worse results,
+      /// but it is more robust.
       ///
       /// The desired selection rule can be specified as a parameter of the
       /// \ref run() function.
@@ -86,20 +95,21 @@
 
         /// An unvisited node having minimum distance from the current
         /// subtour is selected at each step.
-        /// The algorithm runs in O(n<sup>3</sup>) time using this
+        /// The algorithm runs in O(n<sup>2</sup>) time using this
         /// selection rule.
         NEAREST,
 
         /// An unvisited node having maximum distance from the current
         /// subtour is selected at each step.
-        /// The algorithm runs in O(n<sup>3</sup>) time using this
+        /// The algorithm runs in O(n<sup>2</sup>) time using this
         /// selection rule.
         FARTHEST,
 
         /// An unvisited node whose insertion results in the least
         /// increase of the subtour's total cost is selected at each step.
         /// The algorithm runs in O(n<sup>3</sup>) time using this
-        /// selection rule.
+        /// selection rule, but in most cases, it is almost as fast as
+        /// with other rules.
         CHEAPEST,
 
         /// An unvisited node is selected randomly without any evaluation
@@ -134,22 +144,24 @@
       ///
       /// \return The total cost of the found tour.
       Cost run(SelectionRule rule = FARTHEST) {
-        _path.clear();
+        _tour.clear();
 
         if (_gr.nodeNum() == 0) return _sum = 0;
         else if (_gr.nodeNum() == 1) {
-          _path.push_back(_gr(0));
+          _tour.push_back(_gr(0));
           return _sum = 0;
         }
 
         switch (rule) {
           case NEAREST:
             init(true);
-            start<NearestSelection, DefaultInsertion>();
+            start<ComparingSelection<std::less<Cost> >,
+                  DefaultInsertion>();
             break;
           case FARTHEST:
             init(false);
-            start<FarthestSelection, DefaultInsertion>();
+            start<ComparingSelection<std::greater<Cost> >,
+                  DefaultInsertion>();
             break;
           case CHEAPEST:
             init(true);
@@ -185,7 +197,7 @@
       ///
       /// \pre run() must be called before using this function.
       const std::vector<Node>& tourNodes() const {
-        return _path;
+        return _tour;
       }
 
       /// \brief Gives back the node sequence of the found tour.
@@ -196,7 +208,7 @@
       /// \pre run() must be called before using this function.
       template <typename Container>
       void tourNodes(Container &container) const {
-        container.assign(_path.begin(), _path.end());
+        container.assign(_tour.begin(), _tour.end());
       }
 
       /// \brief Gives back the found tour as a path.
@@ -208,11 +220,11 @@
       template <typename Path>
       void tour(Path &path) const {
         path.clear();
-        for (int i = 0; i < int(_path.size()) - 1; ++i) {
-          path.addBack(_gr.arc(_path[i], _path[i+1]));
+        for (int i = 0; i < int(_tour.size()) - 1; ++i) {
+          path.addBack(_gr.arc(_tour[i], _tour[i+1]));
         }
-        if (int(_path.size()) >= 2) {
-          path.addBack(_gr.arc(_path.back(), _path.front()));
+        if (int(_tour.size()) >= 2) {
+          path.addBack(_gr.arc(_tour.back(), _tour.front()));
         }
       }
 
@@ -224,9 +236,9 @@
       void init(bool min) {
         Edge min_edge = min ? mapMin(_gr, _cost) : mapMax(_gr, _cost);
 
-        _path.clear();
-        _path.push_back(_gr.u(min_edge));
-        _path.push_back(_gr.v(min_edge));
+        _tour.clear();
+        _tour.push_back(_gr.u(min_edge));
+        _tour.push_back(_gr.v(min_edge));
 
         _notused.clear();
         for (NodeIt n(_gr); n!=INVALID; ++n) {
@@ -241,106 +253,82 @@
       // Executes the algorithm
       template <class SelectionFunctor, class InsertionFunctor>
       void start() {
-        SelectionFunctor selectNode(_gr, _cost, _path, _notused);
-        InsertionFunctor insertNode(_gr, _cost, _path, _sum);
+        SelectionFunctor selectNode(_gr, _cost, _tour, _notused);
+        InsertionFunctor insertNode(_gr, _cost, _tour, _sum);
 
         for (int i=0; i<_gr.nodeNum()-2; ++i) {
           insertNode.insert(selectNode.select());
         }
 
-        _sum = _cost[_gr.edge(_path.back(), _path.front())];
-        for (int i = 0; i < int(_path.size())-1; ++i) {
-          _sum += _cost[_gr.edge(_path[i], _path[i+1])];
+        _sum = _cost[_gr.edge(_tour.back(), _tour.front())];
+        for (int i = 0; i < int(_tour.size())-1; ++i) {
+          _sum += _cost[_gr.edge(_tour[i], _tour[i+1])];
         }
       }
 
 
-      // Implementation of the nearest selection rule
-      class NearestSelection {
+      // Implementation of the nearest and farthest selection rule
+      template <typename Comparator>
+      class ComparingSelection {
         public:
-          NearestSelection(const FullGraph &gr, const CostMap &cost,
-                           std::vector<Node> &path, std::vector<Node> &notused)
-            : _gr(gr), _cost(cost), _path(path), _notused(notused) {}
-
-          Node select() const {
-            Cost insert_val = 0;
-            int insert_node = -1;
-
+          ComparingSelection(const FullGraph &gr, const CostMap &cost,
+                  std::vector<Node> &tour, std::vector<Node> &notused)
+            : _gr(gr), _cost(cost), _tour(tour), _notused(notused),
+              _dist(gr, 0), _compare()
+          {
+            // Compute initial distances for the unused nodes
             for (unsigned int i=0; i<_notused.size(); ++i) {
-              Cost min_val = _cost[_gr.edge(_notused[i], _path[0])];
-              int min_node = 0;
-
-              for (unsigned int j=1; j<_path.size(); ++j) {
-                Cost curr = _cost[_gr.edge(_notused[i], _path[j])];
-                if (min_val > curr) {
-                    min_val = curr;
-                    min_node = j;
+              Node u = _notused[i];
+              Cost min_dist = _cost[_gr.edge(u, _tour[0])];
+              for (unsigned int j=1; j<_tour.size(); ++j) {
+                Cost curr = _cost[_gr.edge(u, _tour[j])];
+                if (curr < min_dist) {
+                  min_dist = curr;
                 }
               }
+              _dist[u] = min_dist;
+            }
+          }
 
-              if (insert_val > min_val || insert_node == -1) {
-                insert_val = min_val;
-                insert_node = i;
+          Node select() {
+
+            // Select an used node with minimum distance
+            Cost ins_dist = 0;
+            int ins_node = -1;
+            for (unsigned int i=0; i<_notused.size(); ++i) {
+              Cost curr = _dist[_notused[i]];
+              if (_compare(curr, ins_dist) || ins_node == -1) {
+                ins_dist = curr;
+                ins_node = i;
               }
             }
 
-            Node n = _notused[insert_node];
-            _notused.erase(_notused.begin()+insert_node);
+            // Remove the selected node from the unused vector
+            Node sn = _notused[ins_node];
+            _notused[ins_node] = _notused.back();
+            _notused.pop_back();
 
-            return n;
+            // Update the distances of the remaining nodes



More information about the Lemon-commits mailing list