Skip to content

Commit 81ef261

Browse files
committed
add: TSP in graph
1 parent 03a4251 commit 81ef261

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""
2+
title : Travelling Sales man Problem
3+
references : https://en.wikipedia.org/wiki/Travelling_salesman_problem
4+
author : SunayBhoyar
5+
"""
6+
import itertools
7+
import math
8+
9+
demo_graph_points = {
10+
"A": [10, 20],
11+
"B": [30, 21],
12+
"C": [15, 35],
13+
"D": [40, 10],
14+
"E": [25, 5],
15+
"F": [5, 15],
16+
"G": [50, 25]
17+
}
18+
19+
# Euclidean distance - shortest distance between 2 points
20+
def distance(point1, point2):
21+
"""
22+
Calculate the Euclidean distance between two points.
23+
@input: point1, point2 (coordinates of two points as lists [x, y])
24+
@return: Euclidean distance between point1 and point2
25+
@example:
26+
>>> distance([0, 0], [3, 4])
27+
5.0
28+
"""
29+
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
30+
31+
"""
32+
Brute force
33+
Time Complexity - O(n!×n)
34+
Space Complexity - O(n)
35+
"""
36+
def travelling_sales_man_problem_brute_force(graph_points):
37+
"""
38+
Solve the Travelling Salesman Problem using brute force (permutations).
39+
@input: graph_points (dictionary with node names as keys and coordinates as values)
40+
@return: shortest path, total distance (list of nodes representing the shortest path and the distance of that path)
41+
@example:
42+
>>> travelling_sales_man_problem_brute_force({'A': [0, 0], 'B': [0, 1], 'C': [1, 0]})
43+
(['A', 'B', 'C', 'A'], 3.414)
44+
"""
45+
nodes = list(graph_points.keys())
46+
47+
min_path = None
48+
min_distance = float('inf')
49+
50+
# Considering the first Node as the start position
51+
start_node = nodes[0]
52+
other_nodes = nodes[1:]
53+
54+
for perm in itertools.permutations(other_nodes):
55+
path = [start_node] + list(perm) + [start_node]
56+
total_distance = 0
57+
for i in range(len(path) - 1):
58+
total_distance += distance(graph_points[path[i]], graph_points[path[i+1]])
59+
60+
if total_distance < min_distance:
61+
min_distance = total_distance
62+
min_path = path
63+
64+
return min_path, min_distance
65+
66+
"""
67+
dynamic_programming
68+
Time Complexity - O(n^2×2^n)
69+
Space Complexity - O(n×2^n)
70+
"""
71+
def travelling_sales_man_problem_dp(graph_points):
72+
"""
73+
Solve the Travelling Salesman Problem using dynamic programming.
74+
@input: graph_points (dictionary with node names as keys and coordinates as values)
75+
@return: shortest path, total distance (list of nodes representing the shortest path and the distance of that path)
76+
@example:
77+
>>> travelling_sales_man_problem_dp({'A': [0, 0], 'B': [0, 1], 'C': [1, 0]})
78+
(['A', 'B', 'C', 'A'], 3.414)
79+
"""
80+
n = len(graph_points)
81+
nodes = list(graph_points.keys())
82+
83+
# Precompute distances between every pair of nodes
84+
dist = [[0] * n for _ in range(n)]
85+
for i in range(n):
86+
for j in range(n):
87+
dist[i][j] = distance(graph_points[nodes[i]], graph_points[nodes[j]])
88+
89+
# dp[mask][i] represents the minimum distance to visit all nodes in the 'mask' set, ending at node i
90+
dp = [[float('inf')] * n for _ in range(1 << n)]
91+
dp[1][0] = 0 # Start at node 0
92+
93+
# Iterate over all subsets of nodes (represented by mask)
94+
for mask in range(1 << n):
95+
for u in range(n):
96+
if mask & (1 << u):
97+
for v in range(n):
98+
if mask & (1 << v) == 0:
99+
next_mask = mask | (1 << v)
100+
dp[next_mask][v] = min(dp[next_mask][v], dp[mask][u] + dist[u][v])
101+
102+
# Reconstruct the path and find the minimum distance to return to the start
103+
final_mask = (1 << n) - 1
104+
min_cost = float('inf')
105+
end_node = -1
106+
107+
# Find the minimum distance from any node back to the starting node
108+
for u in range(1, n):
109+
if min_cost > dp[final_mask][u] + dist[u][0]:
110+
min_cost = dp[final_mask][u] + dist[u][0]
111+
end_node = u
112+
113+
# Reconstruct the path using the dp table
114+
path = []
115+
mask = final_mask
116+
while end_node != 0:
117+
path.append(nodes[end_node])
118+
for u in range(n):
119+
if mask & (1 << u) and dp[mask][end_node] == dp[mask ^ (1 << end_node)][u] + dist[u][end_node]:
120+
mask ^= (1 << end_node)
121+
end_node = u
122+
break
123+
124+
path.append(nodes[0])
125+
path.reverse()
126+
127+
return path, min_cost
128+
129+
130+
if __name__ == "__main__":
131+
print(f"Travelling salesman problem solved using Brute Force:")
132+
path, distance_travelled = travelling_sales_man_problem_brute_force(demo_graph_points)
133+
print(f"Shortest path: {path}")
134+
print(f"Total distance: {distance_travelled:.2f}")
135+
136+
print(f"\nTravelling salesman problem solved using Dynamic Programming:")
137+
path, distance_travelled = travelling_sales_man_problem_dp(demo_graph_points)
138+
print(f"Shortest path: {path}")
139+
print(f"Total distance: {distance_travelled:.2f}")

0 commit comments

Comments
 (0)