-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtsp_solver.py
156 lines (105 loc) · 3.57 KB
/
tsp_solver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from itertools import permutations
import time
from graph import Position, Node, DistanceMatrix
MAX_NUMBER = 10 ** 10
# TODO: TSP by GNN
class TSP():
def __init__(self):
self.distance_matrix = None
self.nodes = []
self.timestamp = str(time.time())
def from_array(self, distance_array):
N = len(distance_array)
self.nodes = [Node(id) for id in range(1, N+1)]
self.distance_matrix = DistanceMatrix(N, distance_array)
def from_file(self, file_name, mode="euc2d"):
with open(file_name) as f:
while True:
line = f.readline()
line = line.strip()
if not line:
break
if not line[0].isdigit():
# not a node
continue
node = list(filter(
lambda x: x != '',
line.split(' ')))
id = int(node[0])
x, y = map(float, node[1:])
self.nodes.append(Node(id, x, y))
# generate distance matrix N * N
N = len(self.nodes)
self.distance_matrix = DistanceMatrix(N)
for i in range(N):
for j in range(i):
n1 = self.nodes[i]
n2 = self.nodes[j]
self.distance_matrix.set_distance(n1, n2, mode)
def solve_exhaustive(tsp):
# find all permutation of nodes (N!)
# choose path with minimum length
if len(tsp.nodes) < 1:
return []
start_node = tsp.nodes[0]
all_paths = permutations(tsp.nodes[1:])
min_path_length = MAX_NUMBER
min_path = None
for path in all_paths:
path = (start_node,) + path + (start_node,)
path_length = _get_path_length(path, tsp.distance_matrix)
if path_length < min_path_length:
min_path_length = path_length
min_path = path
return min_path[:-1], min_path_length
def solve_dp(tsp):
if len(tsp.nodes) < 1:
return [], 0
# Optimization 1: Fix starting node
start_index = 0
start_node = tsp.nodes[start_index]
N = len(tsp.nodes)
# Optimization 2: Memoize M(i)(visited)
path, length = _get_shortest_path(
start=start_node, last=start_node, visited=(1 << start_index),
tsp=tsp)
path = [start_node] + path
return path, length
def memoize(func):
cache = {}
def memoizer(*args, **kwargs):
key = str(kwargs['last'].id) + \
str(kwargs['visited']) + str(kwargs['tsp'])
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoizer
@memoize
def _get_shortest_path(start, last, visited, tsp):
D = tsp.distance_matrix
nodes = tsp.nodes
N = len(D)
if visited == ((1 << N) - 1):
return [], D.get_distance(last, start)
min_length = MAX_NUMBER
path = None
for i in range(len(D)):
if visited & (1 << i) != 0:
continue
distance = D.get_distance(last, nodes[i])
subpath, length = _get_shortest_path(start=start, last=nodes[i],
visited=visited | (1 << i), tsp=tsp)
length = length + distance
if length < min_length:
min_length = length
path = [nodes[i]] + subpath
return path, min_length
def _get_path_length(path, distance_matrix):
if len(path) < 1:
return 0
prev_node = path[0]
length = 0
for node in path[1:]:
length += distance_matrix.get_distance(node, prev_node)
prev_node = node
return length