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