Divide plane into squares and then local search.

This commit is contained in:
Felix Martin 2020-01-20 18:51:47 -05:00
parent 2bd5774c13
commit 03291a57bc
8 changed files with 78 additions and 39 deletions

View File

@ -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

View 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

File diff suppressed because one or more lines are too long

View 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

View File

View 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

View File

@ -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))