Skip to content

Commit 743f966

Browse files
Add Traveling Salesman Problem (#6205)
1 parent 0072ed9 commit 743f966

File tree

2 files changed

+282
-0
lines changed

2 files changed

+282
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.thealgorithms.graph;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
/**
9+
* This class provides solutions to the Traveling Salesman Problem (TSP) using both brute-force and dynamic programming approaches.
10+
* For more information, see <a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">Wikipedia</a>.
11+
* @author <a href="https://github.com/DenizAltunkapan">Deniz Altunkapan</a>
12+
*/
13+
14+
public final class TravelingSalesman {
15+
16+
// Private constructor to prevent instantiation
17+
private TravelingSalesman() {
18+
}
19+
20+
/**
21+
* Solves the Traveling Salesman Problem (TSP) using brute-force approach.
22+
* This method generates all possible permutations of cities, calculates the total distance for each route, and returns the shortest distance found.
23+
*
24+
* @param distanceMatrix A square matrix where element [i][j] represents the distance from city i to city j.
25+
* @return The shortest possible route distance visiting all cities exactly once and returning to the starting city.
26+
*/
27+
public static int bruteForce(int[][] distanceMatrix) {
28+
if (distanceMatrix.length <= 1) {
29+
return 0;
30+
}
31+
32+
List<Integer> cities = new ArrayList<>();
33+
for (int i = 1; i < distanceMatrix.length; i++) {
34+
cities.add(i);
35+
}
36+
37+
List<List<Integer>> permutations = generatePermutations(cities);
38+
int minDistance = Integer.MAX_VALUE;
39+
40+
for (List<Integer> permutation : permutations) {
41+
List<Integer> route = new ArrayList<>();
42+
route.add(0);
43+
route.addAll(permutation);
44+
int currentDistance = calculateDistance(distanceMatrix, route);
45+
if (currentDistance < minDistance) {
46+
minDistance = currentDistance;
47+
}
48+
}
49+
50+
return minDistance;
51+
}
52+
53+
/**
54+
* Computes the total distance of a given route.
55+
*
56+
* @param distanceMatrix A square matrix where element [i][j] represents the
57+
* distance from city i to city j.
58+
* @param route A list representing the order in which the cities are visited.
59+
* @return The total distance of the route, or Integer.MAX_VALUE if the route is invalid.
60+
*/
61+
public static int calculateDistance(int[][] distanceMatrix, List<Integer> route) {
62+
int distance = 0;
63+
for (int i = 0; i < route.size() - 1; i++) {
64+
int d = distanceMatrix[route.get(i)][route.get(i + 1)];
65+
if (d == Integer.MAX_VALUE) {
66+
return Integer.MAX_VALUE;
67+
}
68+
distance += d;
69+
}
70+
int returnDist = distanceMatrix[route.get(route.size() - 1)][route.get(0)];
71+
return (returnDist == Integer.MAX_VALUE) ? Integer.MAX_VALUE : distance + returnDist;
72+
}
73+
74+
/**
75+
* Generates all permutations of a given list of cities.
76+
*
77+
* @param cities A list of cities to permute.
78+
* @return A list of all possible permutations.
79+
*/
80+
private static List<List<Integer>> generatePermutations(List<Integer> cities) {
81+
List<List<Integer>> permutations = new ArrayList<>();
82+
permute(cities, 0, permutations);
83+
return permutations;
84+
}
85+
86+
/**
87+
* Recursively generates permutations using backtracking.
88+
*
89+
* @param arr The list of cities.
90+
* @param k The current index in the permutation process.
91+
* @param output The list to store generated permutations.
92+
*/
93+
private static void permute(List<Integer> arr, int k, List<List<Integer>> output) {
94+
if (k == arr.size()) {
95+
output.add(new ArrayList<>(arr));
96+
return;
97+
}
98+
for (int i = k; i < arr.size(); i++) {
99+
Collections.swap(arr, i, k);
100+
permute(arr, k + 1, output);
101+
Collections.swap(arr, i, k);
102+
}
103+
}
104+
105+
/**
106+
* Solves the Traveling Salesman Problem (TSP) using dynamic programming with the Held-Karp algorithm.
107+
*
108+
* @param distanceMatrix A square matrix where element [i][j] represents the distance from city i to city j.
109+
* @return The shortest possible route distance visiting all cities exactly once and returning to the starting city.
110+
* @throws IllegalArgumentException if the input matrix is not square.
111+
*/
112+
public static int dynamicProgramming(int[][] distanceMatrix) {
113+
if (distanceMatrix.length == 0) {
114+
return 0;
115+
}
116+
int n = distanceMatrix.length;
117+
118+
for (int[] row : distanceMatrix) {
119+
if (row.length != n) {
120+
throw new IllegalArgumentException("Matrix must be square");
121+
}
122+
}
123+
124+
int[][] dp = new int[n][1 << n];
125+
for (int[] row : dp) {
126+
Arrays.fill(row, Integer.MAX_VALUE);
127+
}
128+
dp[0][1] = 0;
129+
130+
for (int mask = 1; mask < (1 << n); mask++) {
131+
for (int u = 0; u < n; u++) {
132+
if ((mask & (1 << u)) == 0 || dp[u][mask] == Integer.MAX_VALUE) {
133+
continue;
134+
}
135+
for (int v = 0; v < n; v++) {
136+
if ((mask & (1 << v)) != 0 || distanceMatrix[u][v] == Integer.MAX_VALUE) {
137+
continue;
138+
}
139+
int newMask = mask | (1 << v);
140+
dp[v][newMask] = Math.min(dp[v][newMask], dp[u][mask] + distanceMatrix[u][v]);
141+
}
142+
}
143+
}
144+
145+
int minDistance = Integer.MAX_VALUE;
146+
int fullMask = (1 << n) - 1;
147+
for (int i = 1; i < n; i++) {
148+
if (dp[i][fullMask] != Integer.MAX_VALUE && distanceMatrix[i][0] != Integer.MAX_VALUE) {
149+
minDistance = Math.min(minDistance, dp[i][fullMask] + distanceMatrix[i][0]);
150+
}
151+
}
152+
153+
return minDistance == Integer.MAX_VALUE ? 0 : minDistance;
154+
}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.thealgorithms.graph;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class TravelingSalesmanTest {
8+
9+
// Test Case 1: A simple distance matrix with 4 cities
10+
@Test
11+
public void testBruteForceSimple() {
12+
int[][] distanceMatrix = {{0, 10, 15, 20}, {10, 0, 35, 25}, {15, 35, 0, 30}, {20, 25, 30, 0}};
13+
int expectedMinDistance = 80;
14+
int result = TravelingSalesman.bruteForce(distanceMatrix);
15+
assertEquals(expectedMinDistance, result);
16+
}
17+
18+
@Test
19+
public void testDynamicProgrammingSimple() {
20+
int[][] distanceMatrix = {{0, 10, 15, 20}, {10, 0, 35, 25}, {15, 35, 0, 30}, {20, 25, 30, 0}};
21+
int expectedMinDistance = 80;
22+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
23+
assertEquals(expectedMinDistance, result);
24+
}
25+
26+
// Test Case 2: A distance matrix with 3 cities
27+
@Test
28+
public void testBruteForceThreeCities() {
29+
int[][] distanceMatrix = {{0, 10, 15}, {10, 0, 35}, {15, 35, 0}};
30+
int expectedMinDistance = 60;
31+
int result = TravelingSalesman.bruteForce(distanceMatrix);
32+
assertEquals(expectedMinDistance, result);
33+
}
34+
35+
@Test
36+
public void testDynamicProgrammingThreeCities() {
37+
int[][] distanceMatrix = {{0, 10, 15}, {10, 0, 35}, {15, 35, 0}};
38+
int expectedMinDistance = 60;
39+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
40+
assertEquals(expectedMinDistance, result);
41+
}
42+
43+
// Test Case 3: A distance matrix with 5 cities (larger input)
44+
@Test
45+
public void testBruteForceFiveCities() {
46+
int[][] distanceMatrix = {{0, 2, 9, 10, 1}, {2, 0, 6, 5, 8}, {9, 6, 0, 4, 3}, {10, 5, 4, 0, 7}, {1, 8, 3, 7, 0}};
47+
int expectedMinDistance = 15;
48+
int result = TravelingSalesman.bruteForce(distanceMatrix);
49+
assertEquals(expectedMinDistance, result);
50+
}
51+
52+
@Test
53+
public void testDynamicProgrammingFiveCities() {
54+
int[][] distanceMatrix = {{0, 2, 9, 10, 1}, {2, 0, 6, 5, 8}, {9, 6, 0, 4, 3}, {10, 5, 4, 0, 7}, {1, 8, 3, 7, 0}};
55+
int expectedMinDistance = 15;
56+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
57+
assertEquals(expectedMinDistance, result);
58+
}
59+
60+
// Test Case 4: A distance matrix with 2 cities (simple case)
61+
@Test
62+
public void testBruteForceTwoCities() {
63+
int[][] distanceMatrix = {{0, 1}, {1, 0}};
64+
int expectedMinDistance = 2;
65+
int result = TravelingSalesman.bruteForce(distanceMatrix);
66+
assertEquals(expectedMinDistance, result);
67+
}
68+
69+
@Test
70+
public void testDynamicProgrammingTwoCities() {
71+
int[][] distanceMatrix = {{0, 1}, {1, 0}};
72+
int expectedMinDistance = 2;
73+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
74+
assertEquals(expectedMinDistance, result);
75+
}
76+
77+
// Test Case 5: A distance matrix with identical distances
78+
@Test
79+
public void testBruteForceEqualDistances() {
80+
int[][] distanceMatrix = {{0, 10, 10, 10}, {10, 0, 10, 10}, {10, 10, 0, 10}, {10, 10, 10, 0}};
81+
int expectedMinDistance = 40;
82+
int result = TravelingSalesman.bruteForce(distanceMatrix);
83+
assertEquals(expectedMinDistance, result);
84+
}
85+
86+
@Test
87+
public void testDynamicProgrammingEqualDistances() {
88+
int[][] distanceMatrix = {{0, 10, 10, 10}, {10, 0, 10, 10}, {10, 10, 0, 10}, {10, 10, 10, 0}};
89+
int expectedMinDistance = 40;
90+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
91+
assertEquals(expectedMinDistance, result);
92+
}
93+
94+
// Test Case 6: A distance matrix with only one city
95+
@Test
96+
public void testBruteForceOneCity() {
97+
int[][] distanceMatrix = {{0}};
98+
int expectedMinDistance = 0;
99+
int result = TravelingSalesman.bruteForce(distanceMatrix);
100+
assertEquals(expectedMinDistance, result);
101+
}
102+
103+
@Test
104+
public void testDynamicProgrammingOneCity() {
105+
int[][] distanceMatrix = {{0}};
106+
int expectedMinDistance = 0;
107+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
108+
assertEquals(expectedMinDistance, result);
109+
}
110+
111+
// Test Case 7: Distance matrix with large numbers
112+
@Test
113+
public void testBruteForceLargeNumbers() {
114+
int[][] distanceMatrix = {{0, 1000000, 2000000}, {1000000, 0, 1500000}, {2000000, 1500000, 0}};
115+
int expectedMinDistance = 4500000;
116+
int result = TravelingSalesman.bruteForce(distanceMatrix);
117+
assertEquals(expectedMinDistance, result);
118+
}
119+
120+
@Test
121+
public void testDynamicProgrammingLargeNumbers() {
122+
int[][] distanceMatrix = {{0, 1000000, 2000000}, {1000000, 0, 1500000}, {2000000, 1500000, 0}};
123+
int expectedMinDistance = 4500000;
124+
int result = TravelingSalesman.dynamicProgramming(distanceMatrix);
125+
assertEquals(expectedMinDistance, result);
126+
}
127+
}

0 commit comments

Comments
 (0)