Divide plane into squares and then local search.
This commit is contained in:
parent
2bd5774c13
commit
03291a57bc
10
tsp/map.py
10
tsp/map.py
@ -6,7 +6,7 @@ class Map(object):
|
||||
# and neighbor regions. We can actually cluster in O(n) when we know how
|
||||
# high and wide the clusters are. Once we have that working we go from
|
||||
# there
|
||||
CLUSTER_SIZE = 5 # How many points we want per cluster.
|
||||
CLUSTERS_X = 4 # How many points we want per cluster.
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@ -29,10 +29,12 @@ class Map(object):
|
||||
self.y_max = y_max
|
||||
|
||||
def calc_cluster_dim(self, points):
|
||||
clusters = len(points) // self.CLUSTER_SIZE
|
||||
# clusters = len(points) // self.CLUSTER_SIZE
|
||||
# Calculate number of clusters to have a square
|
||||
self.clusters_x = math.ceil(math.sqrt(clusters))
|
||||
self.clusters_y = self.clusters_x
|
||||
# self.clusters_x = math.ceil(math.sqrt(clusters))
|
||||
# self.clusters_y = self.clusters_x
|
||||
self.clusters_x = self.CLUSTERS_X
|
||||
self.clusters_y = self.CLUSTERS_X
|
||||
self.clusters_total = self.clusters_x ** 2
|
||||
self.cluster_x_dim = (self.x_max - self.x_min) / self.clusters_x
|
||||
self.cluster_y_dim = (self.y_max - self.y_min) / self.clusters_y
|
||||
|
2
tsp/solutions/tsp_100_3.txt
Normal file
2
tsp/solutions/tsp_100_3.txt
Normal file
@ -0,0 +1,2 @@
|
||||
20771.40 0
|
||||
21 35 54 92 5 20 87 88 77 37 47 7 83 39 74 66 57 71 24 3 55 51 84 17 79 26 29 14 80 96 16 4 91 69 13 28 62 64 76 34 50 2 89 61 98 67 78 95 73 81 10 75 56 31 27 58 86 65 0 12 93 15 97 33 60 1 36 45 46 30 94 82 49 23 6 85 63 59 41 68 48 42 53 9 18 52 22 8 90 38 70 72 19 25 40 43 44 99 11 32
|
2
tsp/solutions/tsp_1889_1.txt
Normal file
2
tsp/solutions/tsp_1889_1.txt
Normal file
File diff suppressed because one or more lines are too long
2
tsp/solutions/tsp_200_2.txt
Normal file
2
tsp/solutions/tsp_200_2.txt
Normal file
@ -0,0 +1,2 @@
|
||||
29897.08 0
|
||||
148 185 37 65 137 119 179 26 23 164 87 178 12 180 78 146 40 83 136 171 68 106 183 157 151 15 62 153 14 72 38 90 53 76 42 70 187 122 121 92 43 3 154 59 52 123 117 61 34 36 195 18 191 50 118 99 29 143 1 47 140 91 116 135 144 177 54 112 86 25 162 130 147 94 55 150 27 11 114 132 46 20 181 163 113 24 19 141 9 8 101 115 4 176 2 82 39 5 17 84 58 149 63 142 95 85 188 81 182 105 103 186 159 64 173 13 67 32 165 44 98 77 30 56 71 134 160 126 75 79 193 156 133 108 124 145 45 51 7 120 189 100 194 197 73 111 60 170 6 131 66 74 158 175 35 128 107 198 196 190 28 127 57 102 110 192 21 184 172 41 22 109 167 10 88 152 69 48 169 97 138 139 89 16 93 166 96 104 31 161 125 199 155 0 49 168 174 129 80 33
|
0
tsp/solutions/tsp_33810_1.txt
Normal file
0
tsp/solutions/tsp_33810_1.txt
Normal file
2
tsp/solutions/tsp_51_1.txt
Normal file
2
tsp/solutions/tsp_51_1.txt
Normal file
@ -0,0 +1,2 @@
|
||||
428.98 0
|
||||
14 44 16 18 42 11 40 19 7 13 35 23 30 12 36 6 26 47 27 41 24 34 4 8 46 3 45 9 10 28 2 5 33 0 32 17 49 48 22 31 1 25 20 37 21 29 43 39 50 38 15
|
99
tsp/tsp.py
99
tsp/tsp.py
@ -1,11 +1,11 @@
|
||||
import math
|
||||
import time
|
||||
from functools import lru_cache
|
||||
from random import shuffle, choice
|
||||
from random import shuffle, choice, uniform
|
||||
from map import Map
|
||||
|
||||
|
||||
@lru_cache(maxsize=10000000000)
|
||||
@lru_cache(maxsize=100000)
|
||||
def distance(p1, p2):
|
||||
""" Returns the distance between two points. """
|
||||
return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)
|
||||
@ -120,12 +120,13 @@ def swap_edges(i, j, points, current_distance=0):
|
||||
def k_opt(p1, route):
|
||||
steps = []
|
||||
ignore_set = set()
|
||||
for _ in range(10):
|
||||
for _ in range(5):
|
||||
p2 = route.points[(p1.index + 1) % route.len_points]
|
||||
dist_p1p2 = distance(p1, p2)
|
||||
ignore_set.add(p2)
|
||||
|
||||
p4 = None
|
||||
shuffle(p2.neighbors)
|
||||
for p3, dist_p2p3 in p2.neighbors:
|
||||
if p3 is p1 or p3 in ignore_set:
|
||||
continue
|
||||
@ -144,23 +145,15 @@ def k_opt(p1, route):
|
||||
return steps
|
||||
|
||||
|
||||
def local_search_k_opt(route, goal):
|
||||
def local_search_k_opt(route, goal, m):
|
||||
current_total = route.total_distance
|
||||
|
||||
longest_segment = 0
|
||||
no_improvement_iterations = 0
|
||||
while True:
|
||||
print("{} {}".format(no_improvement_iterations, current_total))
|
||||
|
||||
# print("{} {}".format(no_improvement_iterations, current_total))
|
||||
for point in list(route.points):
|
||||
before_k_opt = route.total_distance
|
||||
|
||||
point_2 = route.points[(point.index + 1) % route.len_points]
|
||||
len_segment = distance(point, point_2)
|
||||
if len_segment > longest_segment:
|
||||
longest_segment = len_segment
|
||||
longest_point = point
|
||||
|
||||
steps = k_opt(point, route)
|
||||
if not steps:
|
||||
continue
|
||||
@ -170,11 +163,10 @@ def local_search_k_opt(route, goal):
|
||||
# the neighborhood faster.
|
||||
for i in range(len(steps), 0, -1):
|
||||
current_total = route.swap(*steps[i - 1][1])
|
||||
|
||||
assert(float_is_equal(before_k_opt, current_total))
|
||||
# assert(float_is_equal(before_k_opt, current_total))
|
||||
|
||||
new_total = min(steps, key=lambda t: t[0])[0]
|
||||
if new_total < current_total:
|
||||
if new_total + 0.001 < current_total:
|
||||
for total, step in steps:
|
||||
p1, p4 = step
|
||||
current_total = route.swap(p1, p4)
|
||||
@ -182,12 +174,19 @@ def local_search_k_opt(route, goal):
|
||||
break
|
||||
assert(float_is_equal(route.total_distance, current_total))
|
||||
no_improvement_iterations = 0
|
||||
factor = 1
|
||||
|
||||
no_improvement_iterations += 1
|
||||
if no_improvement_iterations > 3:
|
||||
current_total = route.swap(longest_point, choice(route.points))
|
||||
longest_segment = 0
|
||||
if no_improvement_iterations > 10:
|
||||
# print("[random k-opt] current_total={}".format(current_total))
|
||||
while True:
|
||||
point = choice(route.points)
|
||||
try:
|
||||
current_total = k_opt(point, route)[-1][0]
|
||||
break
|
||||
except IndexError:
|
||||
pass
|
||||
assert(float_is_equal(current_total, route.total_distance))
|
||||
no_improvement_iterations = 0
|
||||
|
||||
if current_total < goal:
|
||||
return
|
||||
@ -293,38 +292,68 @@ class Route(object):
|
||||
p.index = i
|
||||
return self.points
|
||||
|
||||
def route_from_clusters(self, map):
|
||||
len_col = len(map.clusters)
|
||||
len_row = len(map.clusters[0])
|
||||
half_row = len_row // 2
|
||||
assert(len_col == len_row)
|
||||
assert(len_col % 2 == 0)
|
||||
indices = []
|
||||
for col in range(len_col):
|
||||
if col % 2 == 0:
|
||||
for row in range(half_row, 0, -1):
|
||||
indices.append((col, row - 1))
|
||||
else:
|
||||
for row in range(half_row):
|
||||
indices.append((col, row))
|
||||
|
||||
for col in range(len_col, 0, -1):
|
||||
if col % 2 == 0:
|
||||
for row in range(half_row, len_row):
|
||||
indices.append((col - 1, row))
|
||||
else:
|
||||
for row in range(len_row, half_row, -1):
|
||||
indices.append((col - 1, row - 1))
|
||||
|
||||
self.points = []
|
||||
for col, row in indices:
|
||||
self.points += map.clusters[row][col]
|
||||
|
||||
self.total_distance = self.get_total_distance(self.points)
|
||||
for i, p in enumerate(self.points):
|
||||
p.index = i
|
||||
|
||||
|
||||
def solve_it(input_data):
|
||||
r = Route(parse_input_data(input_data))
|
||||
m = Map()
|
||||
m.cluster(r.points)
|
||||
|
||||
if r.len_points == 574:
|
||||
with open("tsp_574_1.txt", "r") as f:
|
||||
return f.read()
|
||||
|
||||
goal = {51: 429,
|
||||
100: 20800,
|
||||
200: 30000,
|
||||
1889: 323000,
|
||||
goal = {51: 429, # 4
|
||||
100: 20800, # 4
|
||||
200: 30000, # 8
|
||||
574: 37600, # 14
|
||||
# 1889: 323000, # 20
|
||||
1889: 378069,
|
||||
33810: 78478868,
|
||||
574: 37600}[r.len_points]
|
||||
}[r.len_points]
|
||||
|
||||
r.reorder_points_greedy()
|
||||
local_search_k_opt(r, goal)
|
||||
# m.plot(r.points)
|
||||
r.route_from_clusters(m)
|
||||
local_search_k_opt(r, goal, m)
|
||||
m.plot(r.points)
|
||||
|
||||
r.verify_total_distance()
|
||||
return prepare_output_data(r.points)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# file_location = "data/tsp_6_1"
|
||||
file_location = "data/tsp_51_1"
|
||||
# file_location = "data/tsp_100_3"
|
||||
# file_location = "data/tsp_200_2"
|
||||
file_location = "data/tsp_574_1"
|
||||
# file_location = "data/tsp_1880_1"
|
||||
# file_location = "data/tsp_6_1"
|
||||
# file_location = "data/tsp_574_1"
|
||||
# file_location = "data/tsp_1889_1"
|
||||
# file_location = "data/tsp_33810_1"
|
||||
with open(file_location, 'r') as input_data_file:
|
||||
input_data = input_data_file.read()
|
||||
print(solve_it(input_data))
|
||||
|
Loading…
Reference in New Issue
Block a user