[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> ¬used)
- : _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> ¬used)
+ : _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