35 /// \brief Insertion algorithm for symmetric TSP. |
36 /// \brief Insertion algorithm for symmetric TSP. |
36 /// |
37 /// |
37 /// InsertionTsp implements the insertion heuristic for solving |
38 /// InsertionTsp implements the insertion heuristic for solving |
38 /// symmetric \ref tsp "TSP". |
39 /// symmetric \ref tsp "TSP". |
39 /// |
40 /// |
40 /// This is a basic TSP heuristic that has many variants. |
41 /// This is a fast and effective tour construction method that has |
|
42 /// many variants. |
41 /// It starts with a subtour containing a few nodes of the graph and it |
43 /// It starts with a subtour containing a few nodes of the graph and it |
42 /// iteratively inserts the other nodes into this subtour according to a |
44 /// iteratively inserts the other nodes into this subtour according to a |
43 /// certain node selection rule. |
45 /// certain node selection rule. |
44 /// |
46 /// |
45 /// This implementation provides four different node selection rules, |
47 /// This method is among the fastest TSP algorithms, and it typically |
46 /// from which the most powerful one is used by default. |
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. |
47 /// For more information, see \ref SelectionRule. |
55 /// For more information, see \ref SelectionRule. |
48 /// |
56 /// |
49 /// \tparam CM Type of the cost map. |
57 /// \tparam CM Type of the cost map. |
50 template <typename CM> |
58 template <typename CM> |
51 class InsertionTsp |
59 class InsertionTsp |
74 /// Enum type containing constants for specifying the node selection |
82 /// Enum type containing constants for specifying the node selection |
75 /// rule for the \ref run() function. |
83 /// rule for the \ref run() function. |
76 /// |
84 /// |
77 /// During the algorithm, nodes are selected for addition to the current |
85 /// During the algorithm, nodes are selected for addition to the current |
78 /// subtour according to the applied rule. |
86 /// subtour according to the applied rule. |
79 /// In general, the FARTHEST method yields the best tours, thus it is the |
87 /// The FARTHEST method is one of the fastest selection rules, and |
80 /// default option. The RANDOM rule usually gives somewhat worse results, |
88 /// it is typically the most effective, thus it is the default |
81 /// but it is much faster than the others and it is the most robust. |
89 /// option. The RANDOM rule usually gives slightly worse results, |
|
90 /// but it is more robust. |
82 /// |
91 /// |
83 /// The desired selection rule can be specified as a parameter of the |
92 /// The desired selection rule can be specified as a parameter of the |
84 /// \ref run() function. |
93 /// \ref run() function. |
85 enum SelectionRule { |
94 enum SelectionRule { |
86 |
95 |
87 /// An unvisited node having minimum distance from the current |
96 /// An unvisited node having minimum distance from the current |
88 /// subtour is selected at each step. |
97 /// subtour is selected at each step. |
89 /// The algorithm runs in O(n<sup>3</sup>) time using this |
98 /// The algorithm runs in O(n<sup>2</sup>) time using this |
90 /// selection rule. |
99 /// selection rule. |
91 NEAREST, |
100 NEAREST, |
92 |
101 |
93 /// An unvisited node having maximum distance from the current |
102 /// An unvisited node having maximum distance from the current |
94 /// subtour is selected at each step. |
103 /// subtour is selected at each step. |
95 /// The algorithm runs in O(n<sup>3</sup>) time using this |
104 /// The algorithm runs in O(n<sup>2</sup>) time using this |
96 /// selection rule. |
105 /// selection rule. |
97 FARTHEST, |
106 FARTHEST, |
98 |
107 |
99 /// An unvisited node whose insertion results in the least |
108 /// An unvisited node whose insertion results in the least |
100 /// increase of the subtour's total cost is selected at each step. |
109 /// increase of the subtour's total cost is selected at each step. |
101 /// The algorithm runs in O(n<sup>3</sup>) time using this |
110 /// The algorithm runs in O(n<sup>3</sup>) time using this |
102 /// selection rule. |
111 /// selection rule, but in most cases, it is almost as fast as |
|
112 /// with other rules. |
103 CHEAPEST, |
113 CHEAPEST, |
104 |
114 |
105 /// An unvisited node is selected randomly without any evaluation |
115 /// An unvisited node is selected randomly without any evaluation |
106 /// at each step. |
116 /// at each step. |
107 /// The global \ref rnd "random number generator instance" is used. |
117 /// The global \ref rnd "random number generator instance" is used. |
132 /// \param rule The node selection rule. For more information, see |
142 /// \param rule The node selection rule. For more information, see |
133 /// \ref SelectionRule. |
143 /// \ref SelectionRule. |
134 /// |
144 /// |
135 /// \return The total cost of the found tour. |
145 /// \return The total cost of the found tour. |
136 Cost run(SelectionRule rule = FARTHEST) { |
146 Cost run(SelectionRule rule = FARTHEST) { |
137 _path.clear(); |
147 _tour.clear(); |
138 |
148 |
139 if (_gr.nodeNum() == 0) return _sum = 0; |
149 if (_gr.nodeNum() == 0) return _sum = 0; |
140 else if (_gr.nodeNum() == 1) { |
150 else if (_gr.nodeNum() == 1) { |
141 _path.push_back(_gr(0)); |
151 _tour.push_back(_gr(0)); |
142 return _sum = 0; |
152 return _sum = 0; |
143 } |
153 } |
144 |
154 |
145 switch (rule) { |
155 switch (rule) { |
146 case NEAREST: |
156 case NEAREST: |
147 init(true); |
157 init(true); |
148 start<NearestSelection, DefaultInsertion>(); |
158 start<ComparingSelection<std::less<Cost> >, |
|
159 DefaultInsertion>(); |
149 break; |
160 break; |
150 case FARTHEST: |
161 case FARTHEST: |
151 init(false); |
162 init(false); |
152 start<FarthestSelection, DefaultInsertion>(); |
163 start<ComparingSelection<std::greater<Cost> >, |
|
164 DefaultInsertion>(); |
153 break; |
165 break; |
154 case CHEAPEST: |
166 case CHEAPEST: |
155 init(true); |
167 init(true); |
156 start<CheapestSelection, CheapestInsertion>(); |
168 start<CheapestSelection, CheapestInsertion>(); |
157 break; |
169 break; |
183 /// This function returns a const reference to a vector |
195 /// This function returns a const reference to a vector |
184 /// that stores the node sequence of the found tour. |
196 /// that stores the node sequence of the found tour. |
185 /// |
197 /// |
186 /// \pre run() must be called before using this function. |
198 /// \pre run() must be called before using this function. |
187 const std::vector<Node>& tourNodes() const { |
199 const std::vector<Node>& tourNodes() const { |
188 return _path; |
200 return _tour; |
189 } |
201 } |
190 |
202 |
191 /// \brief Gives back the node sequence of the found tour. |
203 /// \brief Gives back the node sequence of the found tour. |
192 /// |
204 /// |
193 /// This function copies the node sequence of the found tour into |
205 /// This function copies the node sequence of the found tour into |
194 /// the given standard container. |
206 /// the given standard container. |
195 /// |
207 /// |
196 /// \pre run() must be called before using this function. |
208 /// \pre run() must be called before using this function. |
197 template <typename Container> |
209 template <typename Container> |
198 void tourNodes(Container &container) const { |
210 void tourNodes(Container &container) const { |
199 container.assign(_path.begin(), _path.end()); |
211 container.assign(_tour.begin(), _tour.end()); |
200 } |
212 } |
201 |
213 |
202 /// \brief Gives back the found tour as a path. |
214 /// \brief Gives back the found tour as a path. |
203 /// |
215 /// |
204 /// This function copies the found tour as a list of arcs/edges into |
216 /// This function copies the found tour as a list of arcs/edges into |
239 } |
251 } |
240 |
252 |
241 // Executes the algorithm |
253 // Executes the algorithm |
242 template <class SelectionFunctor, class InsertionFunctor> |
254 template <class SelectionFunctor, class InsertionFunctor> |
243 void start() { |
255 void start() { |
244 SelectionFunctor selectNode(_gr, _cost, _path, _notused); |
256 SelectionFunctor selectNode(_gr, _cost, _tour, _notused); |
245 InsertionFunctor insertNode(_gr, _cost, _path, _sum); |
257 InsertionFunctor insertNode(_gr, _cost, _tour, _sum); |
246 |
258 |
247 for (int i=0; i<_gr.nodeNum()-2; ++i) { |
259 for (int i=0; i<_gr.nodeNum()-2; ++i) { |
248 insertNode.insert(selectNode.select()); |
260 insertNode.insert(selectNode.select()); |
249 } |
261 } |
250 |
262 |
251 _sum = _cost[_gr.edge(_path.back(), _path.front())]; |
263 _sum = _cost[_gr.edge(_tour.back(), _tour.front())]; |
252 for (int i = 0; i < int(_path.size())-1; ++i) { |
264 for (int i = 0; i < int(_tour.size())-1; ++i) { |
253 _sum += _cost[_gr.edge(_path[i], _path[i+1])]; |
265 _sum += _cost[_gr.edge(_tour[i], _tour[i+1])]; |
254 } |
266 } |
255 } |
267 } |
256 |
268 |
257 |
269 |
258 // Implementation of the nearest selection rule |
270 // Implementation of the nearest and farthest selection rule |
259 class NearestSelection { |
271 template <typename Comparator> |
|
272 class ComparingSelection { |
260 public: |
273 public: |
261 NearestSelection(const FullGraph &gr, const CostMap &cost, |
274 ComparingSelection(const FullGraph &gr, const CostMap &cost, |
262 std::vector<Node> &path, std::vector<Node> ¬used) |
275 std::vector<Node> &tour, std::vector<Node> ¬used) |
263 : _gr(gr), _cost(cost), _path(path), _notused(notused) {} |
276 : _gr(gr), _cost(cost), _tour(tour), _notused(notused), |
264 |
277 _dist(gr, 0), _compare() |
265 Node select() const { |
278 { |
266 Cost insert_val = 0; |
279 // Compute initial distances for the unused nodes |
267 int insert_node = -1; |
|
268 |
|
269 for (unsigned int i=0; i<_notused.size(); ++i) { |
280 for (unsigned int i=0; i<_notused.size(); ++i) { |
270 Cost min_val = _cost[_gr.edge(_notused[i], _path[0])]; |
281 Node u = _notused[i]; |
271 int min_node = 0; |
282 Cost min_dist = _cost[_gr.edge(u, _tour[0])]; |
272 |
283 for (unsigned int j=1; j<_tour.size(); ++j) { |
273 for (unsigned int j=1; j<_path.size(); ++j) { |
284 Cost curr = _cost[_gr.edge(u, _tour[j])]; |
274 Cost curr = _cost[_gr.edge(_notused[i], _path[j])]; |
285 if (curr < min_dist) { |
275 if (min_val > curr) { |
286 min_dist = curr; |
276 min_val = curr; |
|
277 min_node = j; |
|
278 } |
287 } |
279 } |
288 } |
280 |
289 _dist[u] = min_dist; |
281 if (insert_val > min_val || insert_node == -1) { |
290 } |
282 insert_val = min_val; |
291 } |
283 insert_node = i; |
292 |
284 } |
293 Node select() { |
285 } |
294 |
286 |
295 // Select an used node with minimum distance |
287 Node n = _notused[insert_node]; |
296 Cost ins_dist = 0; |
288 _notused.erase(_notused.begin()+insert_node); |
297 int ins_node = -1; |
289 |
298 for (unsigned int i=0; i<_notused.size(); ++i) { |
290 return n; |
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; |
291 } |
321 } |
292 |
322 |
293 private: |
323 private: |
294 const FullGraph &_gr; |
324 const FullGraph &_gr; |
295 const CostMap &_cost; |
325 const CostMap &_cost; |
296 std::vector<Node> &_path; |
326 std::vector<Node> &_tour; |
297 std::vector<Node> &_notused; |
327 std::vector<Node> &_notused; |
|
328 FullGraph::NodeMap<Cost> _dist; |
|
329 Comparator _compare; |
298 }; |
330 }; |
299 |
|
300 |
|
301 // Implementation of the farthest selection rule |
|
302 class FarthestSelection { |
|
303 public: |
|
304 FarthestSelection(const FullGraph &gr, const CostMap &cost, |
|
305 std::vector<Node> &path, std::vector<Node> ¬used) |
|
306 : _gr(gr), _cost(cost), _path(path), _notused(notused) {} |
|
307 |
|
308 Node select() const { |
|
309 Cost insert_val = 0; |
|
310 int insert_node = -1; |
|
311 |
|
312 for (unsigned int i=0; i<_notused.size(); ++i) { |
|
313 Cost min_val = _cost[_gr.edge(_notused[i], _path[0])]; |
|
314 int min_node = 0; |
|
315 |
|
316 for (unsigned int j=1; j<_path.size(); ++j) { |
|
317 Cost curr = _cost[_gr.edge(_notused[i], _path[j])]; |
|
318 if (min_val > curr) { |
|
319 min_val = curr; |
|
320 min_node = j; |
|
321 } |
|
322 } |
|
323 |
|
324 if (insert_val < min_val || insert_node == -1) { |
|
325 insert_val = min_val; |
|
326 insert_node = i; |
|
327 } |
|
328 } |
|
329 |
|
330 Node n = _notused[insert_node]; |
|
331 _notused.erase(_notused.begin()+insert_node); |
|
332 |
|
333 return n; |
|
334 } |
|
335 |
|
336 private: |
|
337 const FullGraph &_gr; |
|
338 const CostMap &_cost; |
|
339 std::vector<Node> &_path; |
|
340 std::vector<Node> &_notused; |
|
341 }; |
|
342 |
|
343 |
331 |
344 // Implementation of the cheapest selection rule |
332 // Implementation of the cheapest selection rule |
345 class CheapestSelection { |
333 class CheapestSelection { |
346 private: |
334 private: |
347 Cost costDiff(Node u, Node v, Node w) const { |
335 Cost costDiff(Node u, Node v, Node w) const { |
351 _cost[_gr.edge(u, v)]; |
339 _cost[_gr.edge(u, v)]; |
352 } |
340 } |
353 |
341 |
354 public: |
342 public: |
355 CheapestSelection(const FullGraph &gr, const CostMap &cost, |
343 CheapestSelection(const FullGraph &gr, const CostMap &cost, |
356 std::vector<Node> &path, std::vector<Node> ¬used) |
344 std::vector<Node> &tour, std::vector<Node> ¬used) |
357 : _gr(gr), _cost(cost), _path(path), _notused(notused) {} |
345 : _gr(gr), _cost(cost), _tour(tour), _notused(notused), |
358 |
346 _ins_cost(gr, 0), _ins_pos(gr, -1) |
359 Cost select() const { |
347 { |
360 int insert_notused = -1; |
348 // Compute insertion cost and position for the unused nodes |
361 int best_insert_index = -1; |
|
362 Cost insert_val = 0; |
|
363 |
|
364 for (unsigned int i=0; i<_notused.size(); ++i) { |
349 for (unsigned int i=0; i<_notused.size(); ++i) { |
365 int min = i; |
350 Node u = _notused[i]; |
366 int best_insert_tmp = 0; |
351 Cost min_cost = costDiff(_tour.back(), _tour.front(), u); |
367 Cost min_val = |
352 int min_pos = 0; |
368 costDiff(_path.front(), _path.back(), _notused[i]); |
353 for (unsigned int j=1; j<_tour.size(); ++j) { |
369 |
354 Cost curr_cost = costDiff(_tour[j-1], _tour[j], u); |
370 for (unsigned int j=1; j<_path.size(); ++j) { |
355 if (curr_cost < min_cost) { |
371 Cost tmp = |
356 min_cost = curr_cost; |
372 costDiff(_path[j-1], _path[j], _notused[i]); |
357 min_pos = j; |
373 |
|
374 if (min_val > tmp) { |
|
375 min = i; |
|
376 min_val = tmp; |
|
377 best_insert_tmp = j; |
|
378 } |
358 } |
379 } |
359 } |
380 |
360 _ins_cost[u] = min_cost; |
381 if (insert_val > min_val || insert_notused == -1) { |
361 _ins_pos[u] = min_pos; |
382 insert_notused = min; |
362 } |
383 insert_val = min_val; |
363 } |
384 best_insert_index = best_insert_tmp; |
364 |
385 } |
365 Cost select() { |
386 } |
366 |
387 |
367 // Select an used node with minimum insertion cost |
388 _path.insert(_path.begin()+best_insert_index, |
368 Cost min_cost = 0; |
389 _notused[insert_notused]); |
369 int min_node = -1; |
390 _notused.erase(_notused.begin()+insert_notused); |
370 for (unsigned int i=0; i<_notused.size(); ++i) { |
391 |
371 Cost curr_cost = _ins_cost[_notused[i]]; |
392 return insert_val; |
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; |
393 } |
431 } |
394 |
432 |
395 private: |
433 private: |
396 const FullGraph &_gr; |
434 const FullGraph &_gr; |
397 const CostMap &_cost; |
435 const CostMap &_cost; |
398 std::vector<Node> &_path; |
436 std::vector<Node> &_tour; |
399 std::vector<Node> &_notused; |
437 std::vector<Node> &_notused; |
|
438 FullGraph::NodeMap<Cost> _ins_cost; |
|
439 FullGraph::NodeMap<int> _ins_pos; |
400 }; |
440 }; |
401 |
441 |
402 // Implementation of the random selection rule |
442 // Implementation of the random selection rule |
403 class RandomSelection { |
443 class RandomSelection { |
404 public: |
444 public: |
427 _cost[_gr.edge(u, v)]; |
469 _cost[_gr.edge(u, v)]; |
428 } |
470 } |
429 |
471 |
430 public: |
472 public: |
431 DefaultInsertion(const FullGraph &gr, const CostMap &cost, |
473 DefaultInsertion(const FullGraph &gr, const CostMap &cost, |
432 std::vector<Node> &path, Cost &total_cost) : |
474 std::vector<Node> &tour, Cost &total_cost) : |
433 _gr(gr), _cost(cost), _path(path), _total(total_cost) {} |
475 _gr(gr), _cost(cost), _tour(tour), _total(total_cost) {} |
434 |
476 |
435 void insert(Node n) const { |
477 void insert(Node n) const { |
436 int min = 0; |
478 int min = 0; |
437 Cost min_val = |
479 Cost min_val = |
438 costDiff(_path.front(), _path.back(), n); |
480 costDiff(_tour.front(), _tour.back(), n); |
439 |
481 |
440 for (unsigned int i=1; i<_path.size(); ++i) { |
482 for (unsigned int i=1; i<_tour.size(); ++i) { |
441 Cost tmp = costDiff(_path[i-1], _path[i], n); |
483 Cost tmp = costDiff(_tour[i-1], _tour[i], n); |
442 if (tmp < min_val) { |
484 if (tmp < min_val) { |
443 min = i; |
485 min = i; |
444 min_val = tmp; |
486 min_val = tmp; |
445 } |
487 } |
446 } |
488 } |
447 |
489 |
448 _path.insert(_path.begin()+min, n); |
490 _tour.insert(_tour.begin()+min, n); |
449 _total += min_val; |
491 _total += min_val; |
450 } |
492 } |
451 |
493 |
452 private: |
494 private: |
453 const FullGraph &_gr; |
495 const FullGraph &_gr; |
454 const CostMap &_cost; |
496 const CostMap &_cost; |
455 std::vector<Node> &_path; |
497 std::vector<Node> &_tour; |
456 Cost &_total; |
498 Cost &_total; |
457 }; |
499 }; |
458 |
500 |
459 // Implementation of a special insertion method for the cheapest |
501 // Implementation of a special insertion method for the cheapest |
460 // selection rule |
502 // selection rule |