kpeter@1201: /* -*- mode: C++; indent-tabs-mode: nil; -*- kpeter@1201: * kpeter@1201: * This file is a part of LEMON, a generic C++ optimization library. kpeter@1201: * alpar@1270: * Copyright (C) 2003-2013 kpeter@1201: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport kpeter@1201: * (Egervary Research Group on Combinatorial Optimization, EGRES). kpeter@1201: * kpeter@1201: * Permission to use, modify and distribute this software is granted kpeter@1201: * provided that this copyright notice appears in all copies. For kpeter@1201: * precise terms see the accompanying LICENSE file. kpeter@1201: * kpeter@1201: * This software is provided "AS IS" with no warranty of any kind, kpeter@1201: * express or implied, and with no claim as to its suitability for any kpeter@1201: * purpose. kpeter@1201: * kpeter@1201: */ kpeter@1201: f4c3@1199: #ifndef LEMON_INSERTION_TSP_H f4c3@1199: #define LEMON_INSERTION_TSP_H f4c3@1199: kpeter@1201: /// \ingroup tsp kpeter@1201: /// \file kpeter@1201: /// \brief Insertion algorithm for symmetric TSP kpeter@1201: kpeter@1201: #include kpeter@1204: #include f4c3@1199: #include f4c3@1199: #include f4c3@1199: #include f4c3@1199: f4c3@1199: namespace lemon { f4c3@1199: kpeter@1202: /// \ingroup tsp kpeter@1202: /// kpeter@1201: /// \brief Insertion algorithm for symmetric TSP. kpeter@1201: /// kpeter@1201: /// InsertionTsp implements the insertion heuristic for solving kpeter@1201: /// symmetric \ref tsp "TSP". kpeter@1201: /// kpeter@1204: /// This is a fast and effective tour construction method that has kpeter@1204: /// many variants. kpeter@1201: /// It starts with a subtour containing a few nodes of the graph and it kpeter@1201: /// iteratively inserts the other nodes into this subtour according to a kpeter@1201: /// certain node selection rule. kpeter@1201: /// kpeter@1204: /// This method is among the fastest TSP algorithms, and it typically kpeter@1204: /// provides quite good solutions (usually much better than kpeter@1204: /// \ref NearestNeighborTsp and \ref GreedyTsp). kpeter@1204: /// kpeter@1204: /// InsertionTsp implements four different node selection rules, kpeter@1204: /// from which the most effective one (\e farthest \e node \e selection) kpeter@1204: /// is used by default. kpeter@1204: /// With this choice, the algorithm runs in O(n2) time. kpeter@1201: /// For more information, see \ref SelectionRule. kpeter@1201: /// kpeter@1201: /// \tparam CM Type of the cost map. kpeter@1201: template kpeter@1201: class InsertionTsp kpeter@1201: { kpeter@1201: public: f4c3@1199: kpeter@1201: /// Type of the cost map f4c3@1199: typedef CM CostMap; kpeter@1201: /// Type of the edge costs f4c3@1199: typedef typename CM::Value Cost; f4c3@1199: f4c3@1199: private: f4c3@1199: kpeter@1201: GRAPH_TYPEDEFS(FullGraph); kpeter@1201: kpeter@1201: const FullGraph &_gr; kpeter@1201: const CostMap &_cost; kpeter@1201: std::vector _notused; kpeter@1204: std::vector _tour; kpeter@1201: Cost _sum; kpeter@1201: kpeter@1201: public: kpeter@1201: kpeter@1201: /// \brief Constants for specifying the node selection rule. kpeter@1201: /// kpeter@1201: /// Enum type containing constants for specifying the node selection kpeter@1201: /// rule for the \ref run() function. kpeter@1201: /// kpeter@1201: /// During the algorithm, nodes are selected for addition to the current kpeter@1201: /// subtour according to the applied rule. kpeter@1204: /// The FARTHEST method is one of the fastest selection rules, and kpeter@1204: /// it is typically the most effective, thus it is the default kpeter@1204: /// option. The RANDOM rule usually gives slightly worse results, kpeter@1204: /// but it is more robust. kpeter@1201: /// kpeter@1201: /// The desired selection rule can be specified as a parameter of the kpeter@1201: /// \ref run() function. kpeter@1201: enum SelectionRule { kpeter@1201: kpeter@1201: /// An unvisited node having minimum distance from the current kpeter@1201: /// subtour is selected at each step. kpeter@1204: /// The algorithm runs in O(n2) time using this kpeter@1201: /// selection rule. kpeter@1201: NEAREST, kpeter@1201: kpeter@1201: /// An unvisited node having maximum distance from the current kpeter@1201: /// subtour is selected at each step. kpeter@1204: /// The algorithm runs in O(n2) time using this kpeter@1201: /// selection rule. kpeter@1201: FARTHEST, kpeter@1201: kpeter@1201: /// An unvisited node whose insertion results in the least kpeter@1201: /// increase of the subtour's total cost is selected at each step. kpeter@1201: /// The algorithm runs in O(n3) time using this kpeter@1204: /// selection rule, but in most cases, it is almost as fast as kpeter@1204: /// with other rules. kpeter@1201: CHEAPEST, kpeter@1201: kpeter@1201: /// An unvisited node is selected randomly without any evaluation kpeter@1201: /// at each step. kpeter@1201: /// The global \ref rnd "random number generator instance" is used. kpeter@1201: /// You can seed it before executing the algorithm, if you kpeter@1201: /// would like to. kpeter@1201: /// The algorithm runs in O(n2) time using this kpeter@1201: /// selection rule. kpeter@1201: RANDOM kpeter@1201: }; kpeter@1201: kpeter@1201: public: kpeter@1201: kpeter@1201: /// \brief Constructor kpeter@1201: /// kpeter@1201: /// Constructor. kpeter@1201: /// \param gr The \ref FullGraph "full graph" the algorithm runs on. kpeter@1201: /// \param cost The cost map. kpeter@1201: InsertionTsp(const FullGraph &gr, const CostMap &cost) kpeter@1201: : _gr(gr), _cost(cost) {} kpeter@1201: kpeter@1201: /// \name Execution Control kpeter@1201: /// @{ kpeter@1201: kpeter@1201: /// \brief Runs the algorithm. kpeter@1201: /// kpeter@1201: /// This function runs the algorithm. kpeter@1201: /// kpeter@1201: /// \param rule The node selection rule. For more information, see kpeter@1201: /// \ref SelectionRule. kpeter@1201: /// kpeter@1201: /// \return The total cost of the found tour. kpeter@1201: Cost run(SelectionRule rule = FARTHEST) { kpeter@1204: _tour.clear(); kpeter@1201: kpeter@1201: if (_gr.nodeNum() == 0) return _sum = 0; kpeter@1201: else if (_gr.nodeNum() == 1) { kpeter@1204: _tour.push_back(_gr(0)); kpeter@1201: return _sum = 0; kpeter@1201: } kpeter@1201: kpeter@1201: switch (rule) { kpeter@1201: case NEAREST: kpeter@1201: init(true); kpeter@1204: start >, kpeter@1204: DefaultInsertion>(); kpeter@1201: break; kpeter@1201: case FARTHEST: kpeter@1201: init(false); kpeter@1204: start >, kpeter@1204: DefaultInsertion>(); kpeter@1201: break; kpeter@1201: case CHEAPEST: kpeter@1201: init(true); kpeter@1201: start(); kpeter@1201: break; kpeter@1201: case RANDOM: kpeter@1201: init(true); kpeter@1201: start(); kpeter@1201: break; kpeter@1201: } kpeter@1201: return _sum; kpeter@1201: } kpeter@1201: kpeter@1201: /// @} kpeter@1201: kpeter@1201: /// \name Query Functions kpeter@1201: /// @{ kpeter@1201: kpeter@1201: /// \brief The total cost of the found tour. kpeter@1201: /// kpeter@1201: /// This function returns the total cost of the found tour. kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: Cost tourCost() const { kpeter@1201: return _sum; kpeter@1201: } kpeter@1201: kpeter@1201: /// \brief Returns a const reference to the node sequence of the kpeter@1201: /// found tour. kpeter@1201: /// kpeter@1202: /// This function returns a const reference to a vector kpeter@1201: /// that stores the node sequence of the found tour. kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: const std::vector& tourNodes() const { kpeter@1204: return _tour; kpeter@1201: } kpeter@1201: kpeter@1201: /// \brief Gives back the node sequence of the found tour. kpeter@1201: /// kpeter@1201: /// This function copies the node sequence of the found tour into kpeter@1205: /// an STL container through the given output iterator. The kpeter@1205: /// value_type of the container must be FullGraph::Node. kpeter@1205: /// For example, kpeter@1205: /// \code kpeter@1205: /// std::vector nodes(countNodes(graph)); kpeter@1205: /// tsp.tourNodes(nodes.begin()); kpeter@1205: /// \endcode kpeter@1205: /// or kpeter@1205: /// \code kpeter@1205: /// std::list nodes; kpeter@1205: /// tsp.tourNodes(std::back_inserter(nodes)); kpeter@1205: /// \endcode kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1205: template kpeter@1205: void tourNodes(Iterator out) const { kpeter@1205: std::copy(_tour.begin(), _tour.end(), out); kpeter@1201: } kpeter@1201: kpeter@1201: /// \brief Gives back the found tour as a path. kpeter@1201: /// kpeter@1201: /// This function copies the found tour as a list of arcs/edges into alpar@1250: /// the given \ref lemon::concepts::Path "path structure". kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: template kpeter@1201: void tour(Path &path) const { kpeter@1201: path.clear(); kpeter@1204: for (int i = 0; i < int(_tour.size()) - 1; ++i) { kpeter@1204: path.addBack(_gr.arc(_tour[i], _tour[i+1])); kpeter@1201: } kpeter@1204: if (int(_tour.size()) >= 2) { kpeter@1204: path.addBack(_gr.arc(_tour.back(), _tour.front())); kpeter@1201: } kpeter@1201: } kpeter@1201: kpeter@1201: /// @} kpeter@1201: kpeter@1201: private: kpeter@1201: kpeter@1201: // Initializes the algorithm kpeter@1201: void init(bool min) { kpeter@1201: Edge min_edge = min ? mapMin(_gr, _cost) : mapMax(_gr, _cost); kpeter@1201: kpeter@1204: _tour.clear(); kpeter@1204: _tour.push_back(_gr.u(min_edge)); kpeter@1204: _tour.push_back(_gr.v(min_edge)); kpeter@1201: kpeter@1201: _notused.clear(); kpeter@1201: for (NodeIt n(_gr); n!=INVALID; ++n) { kpeter@1201: if (n != _gr.u(min_edge) && n != _gr.v(min_edge)) { kpeter@1201: _notused.push_back(n); kpeter@1201: } kpeter@1201: } kpeter@1201: kpeter@1201: _sum = _cost[min_edge] * 2; kpeter@1201: } kpeter@1201: kpeter@1201: // Executes the algorithm kpeter@1201: template kpeter@1201: void start() { kpeter@1204: SelectionFunctor selectNode(_gr, _cost, _tour, _notused); kpeter@1204: InsertionFunctor insertNode(_gr, _cost, _tour, _sum); kpeter@1201: kpeter@1201: for (int i=0; i<_gr.nodeNum()-2; ++i) { kpeter@1201: insertNode.insert(selectNode.select()); kpeter@1201: } kpeter@1201: kpeter@1204: _sum = _cost[_gr.edge(_tour.back(), _tour.front())]; kpeter@1204: for (int i = 0; i < int(_tour.size())-1; ++i) { kpeter@1204: _sum += _cost[_gr.edge(_tour[i], _tour[i+1])]; kpeter@1201: } kpeter@1201: } kpeter@1201: kpeter@1201: kpeter@1204: // Implementation of the nearest and farthest selection rule kpeter@1204: template kpeter@1204: class ComparingSelection { f4c3@1199: public: kpeter@1204: ComparingSelection(const FullGraph &gr, const CostMap &cost, kpeter@1204: std::vector &tour, std::vector ¬used) kpeter@1204: : _gr(gr), _cost(cost), _tour(tour), _notused(notused), kpeter@1204: _dist(gr, 0), _compare() kpeter@1204: { kpeter@1204: // Compute initial distances for the unused nodes kpeter@1201: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Node u = _notused[i]; kpeter@1204: Cost min_dist = _cost[_gr.edge(u, _tour[0])]; kpeter@1204: for (unsigned int j=1; j<_tour.size(); ++j) { kpeter@1204: Cost curr = _cost[_gr.edge(u, _tour[j])]; kpeter@1204: if (curr < min_dist) { kpeter@1204: min_dist = curr; kpeter@1201: } kpeter@1201: } kpeter@1204: _dist[u] = min_dist; kpeter@1204: } kpeter@1204: } kpeter@1201: kpeter@1204: Node select() { kpeter@1204: kpeter@1204: // Select an used node with minimum distance kpeter@1204: Cost ins_dist = 0; kpeter@1204: int ins_node = -1; kpeter@1204: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Cost curr = _dist[_notused[i]]; kpeter@1204: if (_compare(curr, ins_dist) || ins_node == -1) { kpeter@1204: ins_dist = curr; kpeter@1204: ins_node = i; f4c3@1199: } f4c3@1199: } kpeter@1201: kpeter@1204: // Remove the selected node from the unused vector kpeter@1204: Node sn = _notused[ins_node]; kpeter@1204: _notused[ins_node] = _notused.back(); kpeter@1204: _notused.pop_back(); kpeter@1201: kpeter@1204: // Update the distances of the remaining nodes kpeter@1204: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Node u = _notused[i]; kpeter@1204: Cost nc = _cost[_gr.edge(sn, u)]; kpeter@1204: if (nc < _dist[u]) { kpeter@1204: _dist[u] = nc; kpeter@1204: } kpeter@1204: } kpeter@1204: kpeter@1204: return sn; f4c3@1199: } f4c3@1199: f4c3@1199: private: f4c3@1199: const FullGraph &_gr; f4c3@1199: const CostMap &_cost; kpeter@1204: std::vector &_tour; f4c3@1199: std::vector &_notused; kpeter@1204: FullGraph::NodeMap _dist; kpeter@1204: Comparator _compare; f4c3@1199: }; f4c3@1199: kpeter@1201: // Implementation of the cheapest selection rule f4c3@1199: class CheapestSelection { f4c3@1199: private: f4c3@1199: Cost costDiff(Node u, Node v, Node w) const { kpeter@1201: return f4c3@1199: _cost[_gr.edge(u, w)] + f4c3@1199: _cost[_gr.edge(v, w)] - f4c3@1199: _cost[_gr.edge(u, v)]; f4c3@1199: } f4c3@1199: f4c3@1199: public: f4c3@1199: CheapestSelection(const FullGraph &gr, const CostMap &cost, kpeter@1204: std::vector &tour, std::vector ¬used) kpeter@1204: : _gr(gr), _cost(cost), _tour(tour), _notused(notused), kpeter@1204: _ins_cost(gr, 0), _ins_pos(gr, -1) kpeter@1204: { kpeter@1204: // Compute insertion cost and position for the unused nodes f4c3@1199: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Node u = _notused[i]; kpeter@1204: Cost min_cost = costDiff(_tour.back(), _tour.front(), u); alpar@1270: int min_pos = 0; kpeter@1204: for (unsigned int j=1; j<_tour.size(); ++j) { kpeter@1204: Cost curr_cost = costDiff(_tour[j-1], _tour[j], u); kpeter@1204: if (curr_cost < min_cost) { kpeter@1204: min_cost = curr_cost; kpeter@1204: min_pos = j; f4c3@1199: } f4c3@1199: } kpeter@1204: _ins_cost[u] = min_cost; kpeter@1204: _ins_pos[u] = min_pos; kpeter@1204: } kpeter@1204: } f4c3@1199: kpeter@1204: Cost select() { kpeter@1204: kpeter@1204: // Select an used node with minimum insertion cost kpeter@1204: Cost min_cost = 0; kpeter@1204: int min_node = -1; kpeter@1204: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Cost curr_cost = _ins_cost[_notused[i]]; kpeter@1204: if (curr_cost < min_cost || min_node == -1) { kpeter@1204: min_cost = curr_cost; kpeter@1204: min_node = i; f4c3@1199: } f4c3@1199: } f4c3@1199: kpeter@1204: // Remove the selected node from the unused vector kpeter@1204: Node sn = _notused[min_node]; kpeter@1204: _notused[min_node] = _notused.back(); kpeter@1204: _notused.pop_back(); alpar@1270: kpeter@1204: // Insert the selected node into the tour kpeter@1204: const int ipos = _ins_pos[sn]; kpeter@1204: _tour.insert(_tour.begin() + ipos, sn); f4c3@1199: kpeter@1204: // Update the insertion cost and position of the remaining nodes kpeter@1204: for (unsigned int i=0; i<_notused.size(); ++i) { kpeter@1204: Node u = _notused[i]; kpeter@1204: Cost curr_cost = _ins_cost[u]; kpeter@1204: int curr_pos = _ins_pos[u]; kpeter@1204: kpeter@1204: int ipos_prev = ipos == 0 ? _tour.size()-1 : ipos-1; kpeter@1204: int ipos_next = ipos == int(_tour.size())-1 ? 0 : ipos+1; kpeter@1204: Cost nc1 = costDiff(_tour[ipos_prev], _tour[ipos], u); kpeter@1204: Cost nc2 = costDiff(_tour[ipos], _tour[ipos_next], u); alpar@1270: kpeter@1204: if (nc1 <= curr_cost || nc2 <= curr_cost) { kpeter@1204: // A new position is better than the old one kpeter@1204: if (nc1 <= nc2) { kpeter@1204: curr_cost = nc1; kpeter@1204: curr_pos = ipos; kpeter@1204: } else { kpeter@1204: curr_cost = nc2; kpeter@1204: curr_pos = ipos_next; kpeter@1204: } kpeter@1204: } kpeter@1204: else { kpeter@1204: if (curr_pos == ipos) { kpeter@1204: // The minimum should be found again kpeter@1204: curr_cost = costDiff(_tour.back(), _tour.front(), u); alpar@1270: curr_pos = 0; kpeter@1204: for (unsigned int j=1; j<_tour.size(); ++j) { kpeter@1204: Cost tmp_cost = costDiff(_tour[j-1], _tour[j], u); kpeter@1204: if (tmp_cost < curr_cost) { kpeter@1204: curr_cost = tmp_cost; kpeter@1204: curr_pos = j; kpeter@1204: } kpeter@1204: } kpeter@1204: } kpeter@1204: else if (curr_pos > ipos) { kpeter@1204: ++curr_pos; kpeter@1204: } kpeter@1204: } alpar@1270: kpeter@1204: _ins_cost[u] = curr_cost; kpeter@1204: _ins_pos[u] = curr_pos; kpeter@1204: } kpeter@1204: kpeter@1204: return min_cost; f4c3@1199: } kpeter@1201: f4c3@1199: private: f4c3@1199: const FullGraph &_gr; f4c3@1199: const CostMap &_cost; kpeter@1204: std::vector &_tour; f4c3@1199: std::vector &_notused; kpeter@1204: FullGraph::NodeMap _ins_cost; kpeter@1204: FullGraph::NodeMap _ins_pos; f4c3@1199: }; f4c3@1199: kpeter@1201: // Implementation of the random selection rule f4c3@1199: class RandomSelection { f4c3@1199: public: f4c3@1199: RandomSelection(const FullGraph &, const CostMap &, kpeter@1201: std::vector &, std::vector ¬used) kpeter@1201: : _notused(notused) {} kpeter@1201: f4c3@1199: Node select() const { f4c3@1199: const int index = rnd[_notused.size()]; f4c3@1199: Node n = _notused[index]; kpeter@1204: _notused[index] = _notused.back(); kpeter@1204: _notused.pop_back(); f4c3@1199: return n; f4c3@1199: } kpeter@1204: f4c3@1199: private: f4c3@1199: std::vector &_notused; f4c3@1199: }; f4c3@1199: f4c3@1199: kpeter@1201: // Implementation of the default insertion method kpeter@1201: class DefaultInsertion { f4c3@1199: private: f4c3@1199: Cost costDiff(Node u, Node v, Node w) const { kpeter@1201: return f4c3@1199: _cost[_gr.edge(u, w)] + f4c3@1199: _cost[_gr.edge(v, w)] - f4c3@1199: _cost[_gr.edge(u, v)]; f4c3@1199: } kpeter@1201: f4c3@1199: public: kpeter@1201: DefaultInsertion(const FullGraph &gr, const CostMap &cost, kpeter@1204: std::vector &tour, Cost &total_cost) : kpeter@1204: _gr(gr), _cost(cost), _tour(tour), _total(total_cost) {} kpeter@1201: f4c3@1199: void insert(Node n) const { f4c3@1199: int min = 0; f4c3@1199: Cost min_val = kpeter@1204: costDiff(_tour.front(), _tour.back(), n); kpeter@1201: kpeter@1204: for (unsigned int i=1; i<_tour.size(); ++i) { kpeter@1204: Cost tmp = costDiff(_tour[i-1], _tour[i], n); f4c3@1199: if (tmp < min_val) { f4c3@1199: min = i; f4c3@1199: min_val = tmp; f4c3@1199: } f4c3@1199: } kpeter@1201: kpeter@1204: _tour.insert(_tour.begin()+min, n); kpeter@1201: _total += min_val; f4c3@1199: } kpeter@1201: f4c3@1199: private: f4c3@1199: const FullGraph &_gr; f4c3@1199: const CostMap &_cost; kpeter@1204: std::vector &_tour; kpeter@1201: Cost &_total; f4c3@1199: }; kpeter@1201: kpeter@1201: // Implementation of a special insertion method for the cheapest kpeter@1201: // selection rule kpeter@1201: class CheapestInsertion { f4c3@1199: TEMPLATE_GRAPH_TYPEDEFS(FullGraph); f4c3@1199: public: kpeter@1201: CheapestInsertion(const FullGraph &, const CostMap &, kpeter@1201: std::vector &, Cost &total_cost) : kpeter@1201: _total(total_cost) {} kpeter@1201: f4c3@1199: void insert(Cost diff) const { kpeter@1201: _total += diff; f4c3@1199: } f4c3@1199: f4c3@1199: private: kpeter@1201: Cost &_total; kpeter@1201: }; kpeter@1201: f4c3@1199: }; kpeter@1201: f4c3@1199: }; // namespace lemon f4c3@1199: f4c3@1199: #endif