2121//
2222// The Shortest Hamiltonian Path Problem (SHPP) is similar to the Traveling
2323// Salesperson Problem (TSP).
24- // You have to visit all the cities, starting from a given kOne and you
24+ // You have to visit all the cities, starting from a given one and you
2525// do not need to return to your starting point. With the TSP, you can start
2626// anywhere, but you have to return to your start location.
2727//
4141// Here is how the algorithm works:
4242// Let us denote the nodes to be visited by their indices 0 .. n - 1
4343// Let us pick 0 as the starting node.
44- // Let d(i,j) denote the distance (or cost) from i to j.
45- // f(S, j) where S is a set of nodes and j is a node in S is defined as follows:
44+ // Let cost(i,j) denote the cost (or distance) to go from i to j.
45+ // f(S, j), where S is a set of nodes and j is a node in S, is defined as the
46+ // total cost of the shortest path from 0 to j going through all nodes of S.
47+ //
48+ // We can prove easily that it satisfy the following relation:
4649// f(S, j) = min (i in S \ {j}, f(S \ {j}, i) + cost(i, j))
4750// (j is an element of S)
51+ //
4852// Note that this formulation, from the original Held-Karp paper is a bit
49- // different, but equivalent to the kOne used in Caseau and Laburthe, Solving
53+ // different, but equivalent to the one used in Caseau and Laburthe, Solving
5054// Small TSPs with Constraints, 1997, ICLP
5155// f(S, j) = min (i in S, f(S \ {i}, i) + cost(i, j))
5256// (j is not an element of S)
7377// To implement dynamic programming, we store the preceding results of
7478// computing f(S,j) in an array M[Offset(S,j)]. See the comments about
7579// LatticeMemoryManager::BaseOffset() to see how this is computed.
80+ // This is really what brings the performance of the algorithm, because memory
81+ // is accessed in sequential order, without risking to thrash the cache.
7682//
7783// Keywords: Traveling Salesman, Hamiltonian Path, Dynamic Programming,
7884// Held, Karp.
@@ -129,9 +135,9 @@ class Set {
129135 typedef Integer IntegerType;
130136
131137 // Useful constants.
132- static constexpr Integer kOne = static_cast < Integer>( 1 ) ;
133- static constexpr Integer kZero = static_cast < Integer>( 0 ) ;
134- static const int MaxCardinality = 8 * sizeof ( Integer); // NOLINT
138+ static constexpr Integer kOne = Integer{ 1 } ;
139+ static constexpr Integer kZero = Integer{ 0 } ;
140+ static constexpr int kMaxCardinality = std::numeric_limits< Integer>::digits;
135141
136142 // Construct a set from an Integer.
137143 explicit Set (Integer n) : value_(n) {
@@ -143,7 +149,7 @@ class Set {
143149 Integer value () const { return value_; }
144150
145151 static Set FullSet (Integer card) {
146- return card == 0 ? Set (0 ) : Set (~kZero >> (MaxCardinality - card));
152+ return card == 0 ? Set (0 ) : Set (~kZero >> (kMaxCardinality - card));
147153 }
148154
149155 // Returns the singleton set with 'n' as its only element.
@@ -355,12 +361,12 @@ class LatticeMemoryManager {
355361template <typename Set, typename CostType>
356362void LatticeMemoryManager<Set, CostType>::Init(int max_card) {
357363 DCHECK_LT (0 , max_card);
358- DCHECK_GE ( Set::MaxCardinality, max_card );
364+ DCHECK_LE (max_card, Set::kMaxCardinality );
359365 if (max_card <= max_card_) return ;
360366 max_card_ = max_card;
361367 binomial_coefficients_.resize (max_card_ + 1 );
362368
363- // Initialize binomial_coefficients_ using Pascal's triangle recursion.
369+ // Initialize binomial_coefficients_ using Pascal's triangle recurrence
364370 for (int n = 0 ; n <= max_card_; ++n) {
365371 binomial_coefficients_[n].resize (n + 2 );
366372 binomial_coefficients_[n][0 ] = 1 ;
@@ -418,7 +424,7 @@ uint64_t LatticeMemoryManager<Set, CostType>::BaseOffset(int card,
418424 DCHECK_EQ (card, node_rank);
419425 // Note(user): It is possible to get rid of base_offset_[card] by using a 2-D
420426 // array. It would also make it possible to free all the memory but the layer
421- // being constructed and the preceding kOne , if another lattice of paths is
427+ // being constructed and the preceding one , if another lattice of paths is
422428 // constructed.
423429 // TODO(user): Evaluate the interest of the above.
424430 // There are 'card' f(set, j) to store. That is why we need to multiply
@@ -465,14 +471,14 @@ class HamiltonianPathSolver {
465471 // stored
466472 public:
467473 // In 2010, 26 was the maximum solvable with 24 Gigs of RAM, and it took
468- // several minutes. With this 2014 version of the code, kOne may go a little
474+ // several minutes. With this 2014 version of the code, one may go a little
469475 // higher, but considering the complexity of the algorithm (n*2^n), and that
470476 // there are very good ways to solve TSP with more than 32 cities,
471477 // we limit ourselves to 32 cites.
472478 // This is why we define the type NodeSet to be 32-bit wide.
473479 // TODO(user): remove this limitation by using pruning techniques.
474- typedef uint32_t Integer;
475- typedef Set<Integer> NodeSet ;
480+ using Integer = uint32_t ;
481+ using NodeSet = Set<Integer>;
476482
477483 explicit HamiltonianPathSolver (CostFunction cost);
478484 HamiltonianPathSolver (int num_nodes, CostFunction cost);
@@ -553,7 +559,7 @@ class HamiltonianPathSolver {
553559 // Returns the cost value between two nodes.
554560 CostType Cost (int i, int j) { return cost_ (i, j); }
555561
556- // Does all the Dynamic Progamming iterations.
562+ // Does all the Dynamic Programming iterations.
557563 void Solve ();
558564
559565 // Computes a path by looking at the information in mem_.
@@ -618,7 +624,7 @@ HamiltonianPathSolver<CostType, CostFunction>::HamiltonianPathSolver(
618624 robustness_checked_(false ),
619625 triangle_inequality_checked_(false ),
620626 solved_(false ) {
621- CHECK_GE (NodeSet::MaxCardinality , num_nodes_);
627+ CHECK_GE (NodeSet::kMaxCardinality , num_nodes_);
622628 CHECK (cost_.Check ());
623629}
624630
@@ -636,7 +642,7 @@ void HamiltonianPathSolver<CostType, CostFunction>::ChangeCostMatrix(
636642 solved_ = false ;
637643 cost_.Reset (cost);
638644 num_nodes_ = num_nodes;
639- CHECK_GE (NodeSet::MaxCardinality , num_nodes_);
645+ CHECK_GE (NodeSet::kMaxCardinality , num_nodes_);
640646 CHECK (cost_.Check ());
641647}
642648
@@ -701,7 +707,7 @@ void HamiltonianPathSolver<CostType, CostFunction>::Solve() {
701707
702708 const NodeSet full_set = NodeSet::FullSet (num_nodes_);
703709
704- // Get the cost of the tsp from node 0. It is the path that leaves 0 and goes
710+ // Get the cost of the TSP from node 0. It is the path that leaves 0 and goes
705711 // through all other nodes, and returns at 0, with minimal cost.
706712 tsp_cost_ = mem_.Value (full_set, 0 );
707713 tsp_path_ = ComputePath (tsp_cost_, full_set, 0 );
@@ -710,7 +716,7 @@ void HamiltonianPathSolver<CostType, CostFunction>::Solve() {
710716 hamiltonian_costs_.resize (num_nodes_);
711717 // Compute the cost of the Hamiltonian paths starting from node 0, going
712718 // through all the other nodes, and ending at end_node. Compute the minimum
713- // kOne along the way.
719+ // one along the way.
714720 CostType min_hamiltonian_cost = std::numeric_limits<CostType>::max ();
715721 const NodeSet hamiltonian_set = full_set.RemoveElement (0 );
716722 for (int end_node : hamiltonian_set) {
@@ -885,7 +891,7 @@ class PruningHamiltonianSolver {
885891 // guaranteed to be smaller than or equal to the cost of Hamiltonian path,
886892 // because Hamiltonian path is a spanning tree itself.
887893
888- // TODO(user): Use generic map-based cache instead of lattice-based kOne .
894+ // TODO(user): Use generic map-based cache instead of lattice-based one .
889895 // TODO(user): Use SaturatedArithmetic for better precision.
890896
891897 public:
0 commit comments