Skip to content

Commit d3d010f

Browse files
committed
TSP under Graphs
1 parent e2a78d4 commit d3d010f

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed

graphs/travelling_salesman.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import heapq
2+
3+
def tsp(cost):
4+
"""
5+
https://www.geeksforgeeks.org/dsa/approximate-solution-for-travelling-salesman-problem-using-mst/
6+
7+
Problem definition:
8+
Given a 2d matrix cost[][] of size n where cost[i][j] denotes the cost of moving from city i to city j.
9+
The task is to complete a tour from city 0 to all other towns such that we visit each city exactly once
10+
and then return to city 0 at minimum cost.
11+
12+
Both the Naive and Dynamic Programming solutions for this problem are infeasible.
13+
In fact, there is no polynomial time solution available for this problem as it is a known NP-Hard problem.
14+
15+
There are approximate algorithms to solve the problem though; for example, the Minimum Spanning Tree (MST) based
16+
approximation algorithm defined below which gives a solution that is at most twice the cost of the optimal solution.
17+
18+
Assumptions:
19+
1. The graph is complete.
20+
21+
2. The problem instance satisfies Triangle-Inequality.(The least distant path to reach a vertex j from i is always to reach j
22+
directly from i, rather than through some other vertex k)
23+
24+
3. The cost matrix is symmetric, i.e., cost[i][j] = cost[j][i]
25+
26+
Time complexity: O(n ^ 3), the time complexity of triangleInequality() function is O(n ^ 3) as we are using 3 nested loops.
27+
Space Complexity: O(n ^ 2), to store the adjacency list, and creating MST.
28+
29+
"""
30+
# create the adjacency list
31+
adj = createList(cost)
32+
33+
#check for triangle inequality violations
34+
if triangleInequality(adj):
35+
print("Triangle Inequality Violation")
36+
return -1
37+
38+
# construct the travelling salesman tour
39+
tspTour = approximateTSP(adj)
40+
41+
# calculate the cost of the tour
42+
tspCost = tourCost(tspTour)
43+
44+
return tspCost
45+
46+
# function to implement approximate TSP
47+
def approximateTSP(adj):
48+
n = len(adj)
49+
50+
# to store the cost of minimum spanning tree
51+
mstCost = [0]
52+
53+
# stores edges of minimum spanning tree
54+
mstEdges = findMST(adj, mstCost)
55+
56+
# to mark the visited nodes
57+
visited = [False] * n
58+
59+
# create adjacency list for mst
60+
mstAdj = [[] for _ in range(n)]
61+
for e in mstEdges:
62+
mstAdj[e[0]].append([e[1], e[2]])
63+
mstAdj[e[1]].append([e[0], e[2]])
64+
65+
# to store the eulerian tour
66+
tour = []
67+
eulerianCircuit(mstAdj, 0, tour, visited, -1)
68+
69+
# add the starting node to the tour
70+
tour.append(0)
71+
72+
# to store the final tour path
73+
tourPath = []
74+
75+
for i in range(len(tour) - 1):
76+
u = tour[i]
77+
v = tour[i + 1]
78+
weight = 0
79+
80+
# find the weight of the edge u -> v
81+
for neighbor in adj[u]:
82+
if neighbor[0] == v:
83+
weight = neighbor[1]
84+
break
85+
86+
# add the edge to the tour path
87+
tourPath.append([u, v, weight])
88+
89+
return tourPath
90+
91+
def tourCost(tour):
92+
cost = 0
93+
for edge in tour:
94+
cost += edge[2]
95+
return cost
96+
97+
98+
def eulerianCircuit(adj, u, tour, visited, parent):
99+
visited[u] = True
100+
tour.append(u)
101+
102+
for neighbor in adj[u]:
103+
v = neighbor[0]
104+
if v == parent:
105+
continue
106+
107+
if visited[v] == False:
108+
eulerianCircuit(adj, v, tour, visited, u)
109+
110+
# function to find the minimum spanning tree
111+
def findMST(adj, mstCost):
112+
n = len(adj)
113+
114+
# to marks the visited nodes
115+
visited = [False] * n
116+
117+
# stores edges of minimum spanning tree
118+
mstEdges = []
119+
120+
pq = []
121+
heapq.heappush(pq, [0, 0, -1])
122+
123+
while pq:
124+
current = heapq.heappop(pq)
125+
126+
u = current[1]
127+
weight = current[0]
128+
parent = current[2]
129+
130+
if visited[u]:
131+
continue
132+
133+
mstCost[0] += weight
134+
visited[u] = True
135+
136+
if parent != -1:
137+
mstEdges.append([u, parent, weight])
138+
139+
for neighbor in adj[u]:
140+
v = neighbor[0]
141+
if v == parent:
142+
continue
143+
w = neighbor[1]
144+
145+
if not visited[v]:
146+
heapq.heappush(pq, [w, v, u])
147+
return mstEdges
148+
149+
150+
151+
# function to calculate if the
152+
# triangle inequality is violated
153+
def triangleInequality(adj):
154+
n = len(adj)
155+
156+
# Sort each adjacency list based
157+
# on the weight of the edges
158+
for i in range(n):
159+
adj[i].sort(key=lambda a: a[1])
160+
161+
# check triangle inequality for each
162+
# triplet of nodes (u, v, w)
163+
for u in range(n):
164+
for x in adj[u]:
165+
v = x[0]
166+
costUV = x[1]
167+
for y in adj[v]:
168+
w = y[0]
169+
costVW = y[1]
170+
for z in adj[u]:
171+
if z[0] == w:
172+
costUW = z[1]
173+
if (costUV + costVW < costUW) and (u < w):
174+
return True
175+
# no violations found
176+
return False
177+
178+
# function to create the adjacency list
179+
def createList(cost):
180+
n = len(cost)
181+
182+
# to store the adjacency list
183+
adj = [[] for _ in range(n)]
184+
185+
for u in range(n):
186+
for v in range(n):
187+
# if there is no edge between u and v
188+
if cost[u][v] == 0:
189+
continue
190+
# add the edge to the adjacency list
191+
adj[u].append([v, cost[u][v]])
192+
193+
return adj
194+
195+
196+
197+
if __name__ == "__main__":
198+
#test
199+
cost = [
200+
[0, 1000, 5000],
201+
[5000, 0, 1000],
202+
[1000, 5000, 0]
203+
]
204+
205+
print(tsp(cost))
206+

0 commit comments

Comments
 (0)