1+ import numpy as np
2+
3+ # convert A of input to 0, when output
4+
5+ class TSP :
6+ def __init__ (self , weights , tour , verticesMapping ):
7+ self .tour = tour # preorder traversal of MST, ordered list of ints
8+ self .weights = weights # np 2D array of ints, cost matrix, keys are ints
9+ self .d = self .calculateWeight (tour )
10+ self .verticesMapping = verticesMapping
11+ print (f"Tour: { self .printTour (self .tour )} , total distance: { self .d } " )
12+
13+ # weight of the input tour
14+ def calculateWeight (self , tour ):
15+ ret = 0
16+ for idx in range (len (tour )):
17+ v_1 = tour [idx ]
18+ if idx == len (tour )- 1 :
19+ v_2 = tour [0 ]
20+ else :
21+ v_2 = tour [idx + 1 ]
22+ ret += self .weights [v_1 ][v_2 ]
23+ return ret
24+
25+ # optimize
26+ def optimize (self ):
27+ N = len (self .tour )
28+ for i in range (N - 3 ):
29+ for j in range (i + 2 , N - 1 ):
30+ newTour = np .copy (self .tour )
31+ newTour [i + 1 ] = self .tour [j ]
32+ tmp = i + 2
33+ for k in range (j - 1 , i , - 1 ):
34+ newTour [tmp ] = self .tour [k ]
35+ tmp += 1
36+ newD = self .calculateWeight (newTour )
37+ if newD < self .d :
38+ self .tour = np .copy (newTour )
39+ self .d = newD
40+ print (f"Tour: { self .printTour (self .tour )} , total distance: { self .d } " )
41+ self .optimize () # tail recursive
42+
43+ # map a list of integers to the corresponding list of strings
44+ def printTour (self , tour ):
45+ return list (map (lambda x : self .verticesMapping [x ], tour ))
46+ from collections import defaultdict
47+ from typing import DefaultDict
48+ import heapq
49+ class MST :
50+ def __init__ (self , cost , root ):
51+ self .root = root
52+ self .cost_matrix = cost
53+ self .mst = self .prims ()
54+
55+ def prims (self ):
56+ ret = defaultdict (set )
57+ visited = set ([self .root ])
58+ edges = [(cost , self .root , v_2 ) for v_2 , cost in enumerate (self .cost_matrix [self .root ])]
59+ heapq .heapify (edges )
60+ while edges :
61+ cost , start , to = heapq .heappop (edges )
62+ if to not in visited :
63+ visited .add (to )
64+ ret [start ].add (to )
65+ for to_next , cost in enumerate (self .cost_matrix [to ]):
66+ if to_next not in visited :
67+ heapq .heappush (edges , (self .cost_matrix [to ][to_next ], to , to_next ))
68+ return ret
69+
70+ def preorder (self , root ):
71+ # convert mst a dictionary to list
72+ children = self .mst [root ]
73+ ret = [root ]
74+ for child in children :
75+ ret = ret + self .preorder (child )
76+ return ret
77+
78+ import argparse
79+
80+ if __name__ == '__main__' :
81+ parser = argparse .ArgumentParser (description = "Apply 2-opt algorithm to approximate TSP solution step by step. By Roundofthree." )
82+ # ask for vertices, eg.: A,B,C,D,E
83+ vertices = input ("Enter the vertices separated by single comma: " ).strip ().split ("," ) # ['A', 'B', 'C', 'D', 'E']
84+ verticesMapping = []
85+ N = len (vertices )
86+ for i in range (0 , N ):
87+ verticesMapping .append (vertices [i ])
88+ # ask for cost matrix nxn, eg.:
89+ # A | B | C | D | E
90+ # A -> 0,13,23,4,2,4
91+ # B ->
92+ print (' | ' .join (vertices ))
93+ cost = []
94+ for v in vertices :
95+ input_array = list (map (int , input (v + "-> " ).strip ().split ("," )))
96+ if len (input_array ) == N :
97+ cost .append (input_array )
98+ else :
99+ print ("error" ) # and terminate
100+ np_cost = np .array (cost ).reshape (N ,N )
101+ # calculate MST from np_cost and vertices --> Prim's starting from 0
102+ mst = MST (np_cost , 0 )
103+ mst_preorder = mst .preorder (0 )
104+ # mst_preorder = [0, 1, 2, 3, 4]
105+ # compute preorder traversal of tree as a list
106+ tsp_solver = TSP (np_cost , mst_preorder , verticesMapping )
107+ tsp_solver .optimize ()
108+
109+
110+
111+
112+
113+
0 commit comments