1- /**
2- * An implementation of the traveling salesman problem in Java using dynamic programming to improve
3- * the time complexity from O(n!) to O(n^2 * 2^n).
4- *
5- * <p>Time Complexity: O(n^2 * 2^n) Space Complexity: O(n * 2^n)
6- *
7- * @author William Fiset, william.alexandre.fiset@gmail.com
8- */
91package com .williamfiset .algorithms .graphtheory ;
102
113import java .util .ArrayList ;
124import java .util .Collections ;
135import java .util .List ;
146
7+ /**
8+ * Traveling Salesman Problem — Iterative DP with Bitmask
9+ *
10+ * Given a complete weighted graph of n nodes, find the minimum-cost
11+ * Hamiltonian cycle (a tour that visits every node exactly once and
12+ * returns to the starting node).
13+ *
14+ * This iterative (bottom-up) approach builds solutions for increasing
15+ * subset sizes. For each subset S of visited nodes and each endpoint
16+ * node i in S, we compute the minimum cost to reach i having visited
17+ * exactly the nodes in S. The recurrence is:
18+ *
19+ * memo[next][S | (1 << next)] = min over end in S of
20+ * memo[end][S] + distance[end][next]
21+ *
22+ * After filling the table, we close the tour by connecting back to
23+ * the start node and backtrack through the table to reconstruct
24+ * the optimal path.
25+ *
26+ * See also: {@link TspDynamicProgrammingRecursive} for the top-down variant.
27+ *
28+ * Time: O(n^2 * 2^n)
29+ * Space: O(n * 2^n)
30+ *
31+ * @author William Fiset, william.alexandre.fiset@gmail.com
32+ */
1533public class TspDynamicProgrammingIterative {
1634
1735 private final int N , start ;
@@ -39,67 +57,82 @@ public TspDynamicProgrammingIterative(int start, double[][] distance) {
3957 this .distance = distance ;
4058 }
4159
42- // Returns the optimal tour for the traveling salesman problem.
60+ /**
61+ * Returns the optimal tour for the traveling salesman problem.
62+ *
63+ * @return ordered list of node indices forming the optimal tour
64+ * (starts and ends with the start node)
65+ */
4366 public List <Integer > getTour () {
4467 if (!ranSolver ) solve ();
4568 return tour ;
4669 }
4770
48- // Returns the minimal tour cost.
71+ /**
72+ * Returns the minimal tour cost.
73+ *
74+ * @return the total cost of the optimal Hamiltonian cycle
75+ */
4976 public double getTourCost () {
5077 if (!ranSolver ) solve ();
5178 return minTourCost ;
5279 }
5380
54- // Solves the traveling salesman problem and caches solution.
81+ /**
82+ * Solves the TSP and caches the result. Subsequent calls are no-ops.
83+ *
84+ * Phase 1: Fill the DP table bottom-up for subsets of size 2..N.
85+ * Phase 2: Close the tour by connecting the last node back to start.
86+ * Phase 3: Backtrack through the table to reconstruct the tour.
87+ */
5588 public void solve () {
56-
5789 if (ranSolver ) return ;
5890
5991 final int END_STATE = (1 << N ) - 1 ;
6092 Double [][] memo = new Double [N ][1 << N ];
6193
62- // Add all outgoing edges from the starting node to memo table.
94+ // Phase 1a: Seed the memo table with direct edges from the start node.
95+ // memo[end][{start, end}] = distance from start to end
6396 for (int end = 0 ; end < N ; end ++) {
6497 if (end == start ) continue ;
6598 memo [end ][(1 << start ) | (1 << end )] = distance [start ][end ];
6699 }
67100
101+ // Phase 1b: Build solutions for subsets of increasing size (3..N).
102+ // For each subset, try extending the path to each node in the subset.
68103 for (int r = 3 ; r <= N ; r ++) {
69104 for (int subset : combinations (r , N )) {
70105 if (notIn (start , subset )) continue ;
71106 for (int next = 0 ; next < N ; next ++) {
72107 if (next == start || notIn (next , subset )) continue ;
108+ // Consider all possible previous endpoints
73109 int subsetWithoutNext = subset ^ (1 << next );
74110 double minDist = Double .POSITIVE_INFINITY ;
75111 for (int end = 0 ; end < N ; end ++) {
76112 if (end == start || end == next || notIn (end , subset )) continue ;
77113 double newDistance = memo [end ][subsetWithoutNext ] + distance [end ][next ];
78- if (newDistance < minDist ) {
114+ if (newDistance < minDist )
79115 minDist = newDistance ;
80- }
81116 }
82117 memo [next ][subset ] = minDist ;
83118 }
84119 }
85120 }
86121
87- // Connect tour back to starting node and minimize cost .
122+ // Phase 2: Close the tour — find the cheapest way to return to start .
88123 for (int i = 0 ; i < N ; i ++) {
89124 if (i == start ) continue ;
90125 double tourCost = memo [i ][END_STATE ] + distance [i ][start ];
91- if (tourCost < minTourCost ) {
126+ if (tourCost < minTourCost )
92127 minTourCost = tourCost ;
93- }
94128 }
95129
130+ // Phase 3: Reconstruct the tour by backtracking through the memo table.
96131 int lastIndex = start ;
97132 int state = END_STATE ;
98133 tour .add (start );
99134
100- // Reconstruct TSP path from memo table.
101135 for (int i = 1 ; i < N ; i ++) {
102-
103136 int bestIndex = -1 ;
104137 double bestDist = Double .POSITIVE_INFINITY ;
105138 for (int j = 0 ; j < N ; j ++) {
@@ -122,48 +155,56 @@ public void solve() {
122155 ranSolver = true ;
123156 }
124157
158+ /** Returns true if the given element's bit is not set in the subset bitmask. */
125159 private static boolean notIn (int elem , int subset ) {
126160 return ((1 << elem ) & subset ) == 0 ;
127161 }
128162
129- // This method generates all bit sets of size n where r bits
130- // are set to one. The result is returned as a list of integer masks.
163+ /**
164+ * Generates all bitmasks of n bits where exactly r bits are set.
165+ * Used to enumerate subsets of a given size.
166+ *
167+ * @param r - number of bits to set
168+ * @param n - total number of bits
169+ * @return list of integer bitmasks
170+ */
131171 public static List <Integer > combinations (int r , int n ) {
132172 List <Integer > subsets = new ArrayList <>();
133173 combinations (0 , 0 , r , n , subsets );
134174 return subsets ;
135175 }
136176
137- // To find all the combinations of size r we need to recurse until we have
138- // selected r elements (aka r = 0), otherwise if r != 0 then we still need to select
139- // an element which is found after the position of our last selected element
177+ /**
178+ * Recursively builds combinations by deciding whether to include
179+ * each bit position. Backtracks when not enough positions remain.
180+ */
140181 private static void combinations (int set , int at , int r , int n , List <Integer > subsets ) {
141-
142- // Return early if there are more elements left to select than what is available.
182+ // Not enough positions remaining to pick r more bits
143183 int elementsLeftToPick = n - at ;
144184 if (elementsLeftToPick < r ) return ;
145185
146- // We selected 'r' elements so we found a valid subset!
147186 if (r == 0 ) {
148187 subsets .add (set );
149188 } else {
150189 for (int i = at ; i < n ; i ++) {
151190 // Try including this element
152191 set ^= (1 << i );
153-
154192 combinations (set , i + 1 , r - 1 , n , subsets );
155-
156193 // Backtrack and try the instance where we did not include this element
157194 set ^= (1 << i );
158195 }
159196 }
160197 }
161198
199+ // ==================== Main ====================
200+
162201 public static void main (String [] args ) {
163- // Create adjacency matrix
202+ // Create a 6-node directed graph with a known optimal tour
164203 int n = 6 ;
165204 double [][] distanceMatrix = new double [n ][n ];
166- for (double [] row : distanceMatrix ) java .util .Arrays .fill (row , 10000 );
205+ for (double [] row : distanceMatrix )
206+ java .util .Arrays .fill (row , 10000 );
207+
167208 distanceMatrix [5 ][0 ] = 10 ;
168209 distanceMatrix [1 ][5 ] = 12 ;
169210 distanceMatrix [4 ][1 ] = 2 ;
@@ -175,10 +216,10 @@ public static void main(String[] args) {
175216 TspDynamicProgrammingIterative solver =
176217 new TspDynamicProgrammingIterative (startNode , distanceMatrix );
177218
178- // Prints : [0, 3, 2, 4, 1, 5, 0]
219+ // Tour : [0, 3, 2, 4, 1, 5, 0]
179220 System .out .println ("Tour: " + solver .getTour ());
180221
181- // Print : 42.0
222+ // Tour cost : 42.0
182223 System .out .println ("Tour cost: " + solver .getTourCost ());
183224 }
184225}
0 commit comments