# HG changeset patch # User Peter Kovacs # Date 1294523476 -3600 # Node ID 9a51db0382284a09f447b6d148d29e681cb23e98 # Parent 62ba43576f85fa7947524d8aba59ac0bc2622919 Document and greatly improve TSP algorithms (#386) - Add LEMON headers. - Add Doxygen doc for all classes and their members. - Clarify and unify the public API of the algorithms. - Various small improvements in the implementations to make them clearer and faster. - Avoid using adaptors in ChristofidesTsp. diff -r 62ba43576f85 -r 9a51db038228 lemon/christofides_tsp.h --- a/lemon/christofides_tsp.h Sat Jan 08 22:49:09 2011 +0100 +++ b/lemon/christofides_tsp.h Sat Jan 08 22:51:16 2011 +0100 @@ -1,129 +1,240 @@ +/* -*- mode: C++; indent-tabs-mode: nil; -*- + * + * This file is a part of LEMON, a generic C++ optimization library. + * + * Copyright (C) 2003-2010 + * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport + * (Egervary Research Group on Combinatorial Optimization, EGRES). + * + * Permission to use, modify and distribute this software is granted + * provided that this copyright notice appears in all copies. For + * precise terms see the accompanying LICENSE file. + * + * This software is provided "AS IS" with no warranty of any kind, + * express or implied, and with no claim as to its suitability for any + * purpose. + * + */ + #ifndef LEMON_CHRISTOFIDES_TSP_H #define LEMON_CHRISTOFIDES_TSP_H +/// \ingroup tsp +/// \file +/// \brief Christofides algorithm for symmetric TSP + #include #include -#include #include #include -#include -#include #include namespace lemon { - namespace christofides_helper { - template - L vectorConvert(const std::vector &_path) { - return L(_path.begin(), _path.end()); - } - - template <> - std::vector vectorConvert(const std::vector &_path) { - return _path; - } - } - + /// \brief Christofides algorithm for symmetric TSP. + /// + /// ChristofidesTsp implements Christofides' heuristic for solving + /// symmetric \ref tsp "TSP". + /// + /// This a well-known approximation method for the TSP problem with + /// \ref checkMetricCost() "metric cost function". + /// It yields a tour whose total cost is at most 3/2 of the optimum, + /// but it is usually much better. + /// This implementation runs in O(n3log(n)) time. + /// + /// The algorithm starts with a \ref spantree "minimum cost spanning tree" and + /// finds a \ref MaxWeightedPerfectMatching "minimum cost perfect matching" + /// in the subgraph induced by the nodes that have odd degree in the + /// spanning tree. + /// Finally, it constructs the tour from the \ref EulerIt "Euler traversal" + /// of the union of the spanning tree and the matching. + /// During this last step, the algorithm simply skips the visited nodes + /// (i.e. creates shortcuts) assuming that the triangle inequality holds + /// for the cost function. + /// + /// \tparam CM Type of the cost map. + /// + /// \warning \& CM::Value must be signed type. template - class ChristofidesTsp { + class ChristofidesTsp + { + public: + + /// Type of the cost map + typedef CM CostMap; + /// Type of the edge costs + typedef typename CM::Value Cost; + private: - GRAPH_TYPEDEFS(SmartGraph); + + GRAPH_TYPEDEFS(FullGraph); + + const FullGraph &_gr; + const CostMap &_cost; + std::vector _path; + Cost _sum; public: - typedef typename CM::Value Cost; - typedef SmartGraph::EdgeMap CostMap; - - ChristofidesTsp(const FullGraph &gr, const CM &cost) : _cost(_gr), fullg(gr), fullcost(cost), nr(_gr) { - graphCopy(gr, _gr).nodeCrossRef(nr).edgeMap(cost, _cost).run(); - } + /// \brief Constructor + /// + /// Constructor. + /// \param gr The \ref FullGraph "full graph" the algorithm runs on. + /// \param cost The cost map. + ChristofidesTsp(const FullGraph &gr, const CostMap &cost) + : _gr(gr), _cost(cost) {} + + /// \name Execution Control + /// @{ + + /// \brief Runs the algorithm. + /// + /// This function runs the algorithm. + /// + /// \return The total cost of the found tour. Cost run() { _path.clear(); + + if (_gr.nodeNum() == 0) return _sum = 0; + else if (_gr.nodeNum() == 1) { + _path.push_back(_gr(0)); + return _sum = 0; + } + else if (_gr.nodeNum() == 2) { + _path.push_back(_gr(0)); + _path.push_back(_gr(1)); + return _sum = 2 * _cost[_gr.edge(_gr(0), _gr(1))]; + } - SmartGraph::EdgeMap tree(_gr); - kruskal(_gr, _cost, tree); + // Compute min. cost spanning tree + std::vector tree; + kruskal(_gr, _cost, std::back_inserter(tree)); - FilterEdges treefiltered(_gr, tree); - InDegMap > deg(treefiltered); - - SmartGraph::NodeMap oddfilter(_gr, false); - FilterNodes oddNodes(_gr, oddfilter); - - for (NodeIt n(_gr); n!=INVALID; ++n) { - if (deg[n]%2 == 1) { - oddNodes.enable(n); + FullGraph::NodeMap deg(_gr, 0); + for (int i = 0; i != int(tree.size()); ++i) { + Edge e = tree[i]; + ++deg[_gr.u(e)]; + ++deg[_gr.v(e)]; + } + + // Copy the induced subgraph of odd nodes + std::vector odd_nodes; + for (NodeIt u(_gr); u != INVALID; ++u) { + if (deg[u] % 2 == 1) odd_nodes.push_back(u); + } + + SmartGraph sgr; + SmartGraph::EdgeMap scost(sgr); + for (int i = 0; i != int(odd_nodes.size()); ++i) { + sgr.addNode(); + } + for (int i = 0; i != int(odd_nodes.size()); ++i) { + for (int j = 0; j != int(odd_nodes.size()); ++j) { + if (j == i) continue; + SmartGraph::Edge e = + sgr.addEdge(sgr.nodeFromId(i), sgr.nodeFromId(j)); + scost[e] = -_cost[_gr.edge(odd_nodes[i], odd_nodes[j])]; } } - NegMap negmap(_cost); - MaxWeightedPerfectMatching, - NegMap > perfmatch(oddNodes, negmap); - perfmatch.run(); + // Compute min. cost perfect matching + MaxWeightedPerfectMatching > + mwpm(sgr, scost); + mwpm.run(); - for (FilterNodes::EdgeIt e(oddNodes); e!=INVALID; ++e) { - if (perfmatch.matching(e)) { - treefiltered.enable(_gr.addEdge(_gr.u(e), _gr.v(e))); + for (SmartGraph::EdgeIt e(sgr); e != INVALID; ++e) { + if (mwpm.matching(e)) { + tree.push_back( _gr.edge(odd_nodes[sgr.id(sgr.u(e))], + odd_nodes[sgr.id(sgr.v(e))]) ); } } - FilterEdges::NodeMap seen(treefiltered, false); - for (EulerIt > e(treefiltered); e!=INVALID; ++e) { - if (seen[treefiltered.target(e)]==false) { - _path.push_back(nr[treefiltered.target(e)]); - seen[treefiltered.target(e)] = true; + // Join the spanning tree and the matching + sgr.clear(); + for (int i = 0; i != _gr.nodeNum(); ++i) { + sgr.addNode(); + } + for (int i = 0; i != int(tree.size()); ++i) { + int ui = _gr.id(_gr.u(tree[i])), + vi = _gr.id(_gr.v(tree[i])); + sgr.addEdge(sgr.nodeFromId(ui), sgr.nodeFromId(vi)); + } + + // Compute the tour from the Euler traversal + SmartGraph::NodeMap visited(sgr, false); + for (EulerIt e(sgr); e != INVALID; ++e) { + SmartGraph::Node n = sgr.target(e); + if (!visited[n]) { + _path.push_back(_gr(sgr.id(n))); + visited[n] = true; } } - _sum = fullcost[ fullg.edge(_path.back(), _path.front()) ]; - for (unsigned int i=0; i<_path.size()-1; ++i) - _sum += fullcost[ fullg.edge(_path[i], _path[i+1]) ]; + _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])]; + } return _sum; } - template - void tourNodes(L &container) { - container(christofides_helper::vectorConvert(_path)); - } + /// @} - template