Divide plane into squares and then local search.

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

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