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