Skip to content

Commit b336c50

Browse files
committed
Added iterative TSP
1 parent b9fa28b commit b336c50

File tree

5 files changed

+225
-23
lines changed

5 files changed

+225
-23
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import java.util.*;
2+
public class TspDynamicProgrammingIterative {
3+
4+
public static double tsp(double[][] distance) {
5+
return tsp(0, distance);
6+
}
7+
8+
public static double tsp(int start, double[][] distance) {
9+
final int n = distance.length;
10+
11+
if (n <= 1) return 0;
12+
if (n != distance[0].length) throw new IllegalStateException("Matrix must be square (n x n)");
13+
if (n == 2) return distance[0][1] + distance[1][0];
14+
if (start < 0 || start >= n) throw new IllegalArgumentException("Invalid start node.");
15+
16+
final int END_STATE = (1 << n) - 1;
17+
Double[][] memo = new Double[n][1 << n];
18+
19+
// Add all outgoing edges from the starting node to memo table.
20+
for (int end = 0; end < n; end++) {
21+
if (end == start) continue;
22+
memo[end][(1 << start) | (1 << end)] = distance[start][end];
23+
}
24+
25+
for (int r = 3; r <= n; r++) {
26+
for (int subset : combinations(r, n)) {
27+
if (notIn(start, subset)) continue;
28+
for (int next = 0; next < n; next++) {
29+
if (next == start) continue;
30+
if (notIn(next, subset)) continue;
31+
int subsetWithoutNext = subset & ~(1 << next);
32+
double minDist = Double.POSITIVE_INFINITY;
33+
for (int end = 0; end < n; end++) {
34+
if (end == start || end == next) continue;
35+
if (notIn(end, subset)) continue;
36+
double newDistance = memo[end][subsetWithoutNext] + distance[end][next];
37+
if (newDistance < minDist)
38+
minDist = newDistance;
39+
}
40+
memo[next][subset] = minDist;
41+
}
42+
}
43+
}
44+
45+
// Connect tour back to starting node.
46+
double minTourCost = Double.POSITIVE_INFINITY;
47+
for (int end = 0; end < n; end++) {
48+
if (end == start) continue;
49+
double tourCost = memo[end][END_STATE] + distance[end][start];
50+
if (tourCost < minTourCost) minTourCost = tourCost;
51+
}
52+
53+
return minTourCost;
54+
}
55+
56+
static boolean in(int elem, int subset) {
57+
return ((1 << elem) & subset) != 0;
58+
}
59+
60+
static boolean notIn(int elem, int subset) {
61+
return !in(elem, subset);
62+
}
63+
64+
// This method finds all the combinations of {0,1...n-1} of size 'r'
65+
// returned as a list of integer masks.
66+
public static List<Integer> combinations(int r, int n) {
67+
int[] set = new int[n];
68+
for(int i = 0; i < n; i++) set[i] = i;
69+
List<Integer> subsets = new ArrayList<>();
70+
boolean [] used = new boolean[set.length];
71+
combinations(set, r, 0, used, subsets);
72+
return subsets;
73+
}
74+
75+
// To find all the combinations of size r we need to recurse until we have
76+
// selected r elements (aka r = 0), otherwise if r != 0 then we need to select
77+
// an element which is found after the position of our last selected element
78+
private static void combinations(int[] set, int r, int at, boolean[] used, List<Integer> subsets) {
79+
final int N = set.length;
80+
81+
// Return early if there are more elements left to select than what is available.
82+
int elementsLeftToPick = N - at;
83+
if (elementsLeftToPick < r) return;
84+
85+
// We selected 'r' elements so we found a valid subset!
86+
if (r == 0) {
87+
int subset = 0;
88+
for (int i = 0; i < N; i++)
89+
if (used[i]) subset |= 1 << i;
90+
subsets.add(subset);
91+
} else {
92+
for (int i = at; i < N; i++) {
93+
94+
// Try including this element
95+
used[i] = true;
96+
97+
combinations(set, r - 1, i + 1, used, subsets);
98+
99+
// Backtrack and try the instance where we did not include this element
100+
used[i] = false;
101+
}
102+
}
103+
}
104+
105+
public static void main(String[] args) {
106+
// Create adjacency matrix
107+
int n = 6;
108+
double[][] distanceMatrix = new double[n][n];
109+
for (double[] row : distanceMatrix) java.util.Arrays.fill(row, 10000);
110+
distanceMatrix[0][5] = distanceMatrix[5][0] = 10;
111+
distanceMatrix[5][1] = distanceMatrix[1][5] = 12;
112+
distanceMatrix[1][4] = distanceMatrix[4][1] = 2;
113+
distanceMatrix[4][2] = distanceMatrix[2][4] = 4;
114+
distanceMatrix[2][3] = distanceMatrix[3][2] = 6;
115+
distanceMatrix[3][0] = distanceMatrix[0][3] = 8;
116+
System.out.println(tsp(distanceMatrix));
117+
118+
// int n = 4;
119+
// double[][] distanceMatrix = new double[n][n];
120+
// for (double[] row : distanceMatrix) java.util.Arrays.fill(row, 10000);
121+
// distanceMatrix[0][2] = 1;
122+
// distanceMatrix[2][1] = 2;
123+
// distanceMatrix[1][3] = 4;
124+
// distanceMatrix[3][0] = 8;
125+
// System.out.println(tsp(distanceMatrix));
126+
127+
// int n = 4;
128+
// double[][] m = {
129+
// {0, 2, 9, 10},
130+
// {1, 0, 6, 4},
131+
// {15, 7, 0, 8},
132+
// {6, 3, 12, 0}
133+
// };
134+
// System.out.println(tsp(m));
135+
136+
// printCombs(3, 5);
137+
138+
}
139+
140+
static void printCombs(int r, int n) {
141+
for (Integer x : combinations(r, n)) {
142+
printState(x, n);
143+
}
144+
}
145+
146+
static void printState(int state, int n) {
147+
for (int i = 0; i < n; i++)
148+
if ((state & (1 << i)) != 0)
149+
System.out.print(i + " ");
150+
System.out.println();
151+
}
152+
153+
}
154+
155+
156+
157+
158+
159+
160+
161+
162+
163+
164+
165+

GraphTheory/TspDynamicProgramming.java renamed to GraphTheory/TspDynamicProgrammingRecursive.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import java.util.*;
1818

19-
public class TspDynamicProgramming {
19+
public class TspDynamicProgrammingRecursive {
2020

2121
private final int N;
2222
private final int START_NODE;
@@ -27,11 +27,11 @@ public class TspDynamicProgramming {
2727

2828
private List<Integer> tour = new ArrayList<>();
2929

30-
public TspDynamicProgramming(double[][] distance) {
30+
public TspDynamicProgrammingRecursive(double[][] distance) {
3131
this(0, distance);
3232
}
3333

34-
public TspDynamicProgramming(int startNode, double[][] distance) {
34+
public TspDynamicProgrammingRecursive(int startNode, double[][] distance) {
3535

3636
this.distance = distance;
3737
N = distance.length;
@@ -123,7 +123,7 @@ public static void main(String[] args) {
123123
distanceMatrix[5][1] = distanceMatrix[1][5] = 12;
124124

125125
// Run the solver
126-
TspDynamicProgramming solver = new TspDynamicProgramming(distanceMatrix);
126+
TspDynamicProgrammingRecursive solver = new TspDynamicProgrammingRecursive(distanceMatrix);
127127

128128
// Prints: [0, 3, 2, 4, 1, 5, 0]
129129
System.out.println("Tour: " + solver.getTour());
28.1 KB
Binary file not shown.

GraphTheory/tests/TravelingSalesmanProblemTest.java

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,52 @@ public class TravelingSalesmanProblemTest {
88
private static final double EPS = 1e-5;
99

1010
@Test(expected=IllegalArgumentException.class)
11-
public void testInvalidStartNode() {
11+
public void testTspRecursiveInvalidStartNode() {
1212
double[][] dist = {
1313
{1, 2, 3},
1414
{4, 5, 6},
1515
{7, 8, 9}
1616
};
17-
new TspDynamicProgramming(321, dist);
17+
new TspDynamicProgrammingRecursive(321, dist);
18+
}
19+
20+
@Test(expected=IllegalArgumentException.class)
21+
public void testTspIterativeInvalidStartNode() {
22+
double[][] dist = {
23+
{1, 2, 3},
24+
{4, 5, 6},
25+
{7, 8, 9}
26+
};
27+
TspDynamicProgrammingIterative.tsp(321, dist);
1828
}
1929

2030
@Test(expected=IllegalStateException.class)
21-
public void testNonSquareMatrix() {
31+
public void testTspRecursiveNonSquareMatrix() {
2232
double[][] dist = {
2333
{1, 2, 3},
2434
{4, 5, 6}
2535
};
26-
new TspDynamicProgramming(dist);
36+
new TspDynamicProgrammingRecursive(dist);
2737
}
2838

2939
@Test(expected=IllegalStateException.class)
30-
public void testSmallGraph() {
40+
public void testTspIterativeNonSquareMatrix() {
41+
double[][] dist = {
42+
{1, 2, 3},
43+
{4, 5, 6}
44+
};
45+
TspDynamicProgrammingIterative.tsp(dist);
46+
}
47+
48+
@Test(expected=IllegalStateException.class)
49+
public void testTspRecursiveSmallGraph() {
3150
double[][] dist = {
3251
{0, 1},
3352
{1, 0}
3453
};
35-
new TspDynamicProgramming(dist);
54+
new TspDynamicProgrammingRecursive(dist);
3655
}
37-
56+
3857
@Test
3958
public void testTsp_small1() {
4059
int n = 5;
@@ -48,8 +67,12 @@ public void testTsp_small1() {
4867
dist[2][4] = dist[4][2] = 4;
4968
dist[4][1] = dist[1][4] = 5;
5069

51-
double tourCost = new TspDynamicProgramming(dist).getTourCost();
52-
assertThat(tourCost).isWithin(EPS).of(1+2+3+4+5);
70+
double expected = 1+2+3+4+5;
71+
double tspRecursiveTourCost = new TspDynamicProgrammingRecursive(dist).getTourCost();
72+
double tspIterativeTourCost = TspDynamicProgrammingIterative.tsp(dist);
73+
74+
assertThat(tspRecursiveTourCost).isWithin(EPS).of(expected);
75+
assertThat(tspIterativeTourCost).isWithin(EPS).of(expected);
5376
}
5477

5578
@Test
@@ -60,11 +83,13 @@ public void testDpVsBf() {
6083
double[][] dist = new double[n][n];
6184
randomFillDistMatrix(dist);
6285

63-
TspDynamicProgramming dpSolver = new TspDynamicProgramming(dist);
64-
double dp = dpSolver.getTourCost();
86+
TspDynamicProgrammingRecursive dpSolver = new TspDynamicProgrammingRecursive(dist);
87+
double dp1 = dpSolver.getTourCost();
88+
double dp2 = TspDynamicProgrammingIterative.tsp(dist);
6589
double bf = TspBruteForce.computeTourCost(TspBruteForce.tsp(dist), dist);
6690

67-
assertThat(dp).isWithin(EPS).of(bf);
91+
assertThat(dp1).isWithin(EPS).of(bf);
92+
assertThat(dp2).isWithin(EPS).of(bf);
6893
}
6994
}
7095
}
@@ -77,7 +102,7 @@ public void testGeneratedTour() {
77102
double[][] dist = new double[n][n];
78103
randomFillDistMatrix(dist);
79104

80-
TspDynamicProgramming dpSolver = new TspDynamicProgramming(dist);
105+
TspDynamicProgrammingRecursive dpSolver = new TspDynamicProgrammingRecursive(dist);
81106
int[] bfPath = TspBruteForce.tsp(dist);
82107

83108
double dp = dpSolver.getTourCost();
@@ -99,9 +124,11 @@ public void testDifferentStartingNodes() {
99124
double bf = TspBruteForce.computeTourCost(bfPath, dist);
100125

101126
for (int startNode = 0; startNode < n; startNode++) {
102-
TspDynamicProgramming dpSolver = new TspDynamicProgramming(startNode, dist);
103-
double dp = dpSolver.getTourCost();
104-
assertThat(dp).isWithin(EPS).of(bf);
127+
TspDynamicProgrammingRecursive dpSolver = new TspDynamicProgrammingRecursive(startNode, dist);
128+
double dp1 = dpSolver.getTourCost();
129+
double dp2 = TspDynamicProgrammingIterative.tsp(startNode, dist);
130+
assertThat(dp1).isWithin(EPS).of(bf);
131+
assertThat(dp2).isWithin(EPS).of(bf);
105132
assertThat(getTourCost(dist, dpSolver.getTour())).isWithin(EPS).of(bf);
106133
}
107134

@@ -110,11 +137,20 @@ public void testDifferentStartingNodes() {
110137
}
111138

112139
// Try slightly larger matrices to make sure they run is a reasonable amount of time.
113-
@Test public void testPerformance() {
140+
@Test public void testTspRecursivePerformance() {
141+
for(int n = 3; n <= 16; n++) {
142+
double[][] dist = new double[n][n];
143+
randomFillDistMatrix(dist);
144+
new TspDynamicProgrammingRecursive(dist);
145+
}
146+
}
147+
148+
// Try slightly larger matrices to make sure they run is a reasonable amount of time.
149+
@Test public void testTspIterativePerformance() {
114150
for(int n = 3; n <= 16; n++) {
115151
double[][] dist = new double[n][n];
116152
randomFillDistMatrix(dist);
117-
new TspDynamicProgramming(dist);
153+
TspDynamicProgrammingIterative.tsp(dist);
118154
}
119155
}
120156

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ Uncomment when more AI algorithms are done.
9191
* [Topological sort (acyclic graph, adjacency list)](GraphTheory/TopologicalSortAdjacencyList.java) **- O(V+E)**
9292
* [Topological sort (acyclic graph, adjacency matrix)](GraphTheory/TopologicalSortAdjacencyMatrix.java) **- O(V<sup>2</sup>)**
9393
* [Traveling Salesman Problem (brute force)](GraphTheory/TspBruteForce.java) **- O(n!)**
94-
* [Traveling Salesman Problem (dynamic programming)](GraphTheory/TspDynamicProgramming.java) **- O(n<sup>2</sup>2<sup>n</sup>)**
94+
* [Traveling Salesman Problem (dynamic programming, iterative)](GraphTheory/TspDynamicProgrammingIterative.java) **- O(n<sup>2</sup>2<sup>n</sup>)**
95+
* [Traveling Salesman Problem (dynamic programming, recursive)](GraphTheory/TspDynamicProgrammingRecursive.java) **- O(n<sup>2</sup>2<sup>n</sup>)**
9596

9697
# Linear algebra
9798
* [Freivald's algorithm (matrix multiplication verification)](LinearAlgebra/FreivaldsAlgorithm.java) **- O(kn<sup>2</sup>)**

0 commit comments

Comments
 (0)