Eod commit.
This commit is contained in:
parent
7481ee3f0a
commit
19c1515c0e
@ -8,6 +8,13 @@ def on_segment(p, q, r):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def intersect(a, b, c, d):
|
||||||
|
# Return true if line segments ab and cd intersect
|
||||||
|
def ccw(a, b, c):
|
||||||
|
return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x)
|
||||||
|
return ccw(a, c, d) != ccw(b, c, d) and ccw(a, b, c) != ccw(a, b, d)
|
||||||
|
|
||||||
|
|
||||||
def orientation(p, q, r):
|
def orientation(p, q, r):
|
||||||
# To find orientation of ordered triplet (p, q, r).
|
# To find orientation of ordered triplet (p, q, r).
|
||||||
# The function returns following values
|
# The function returns following values
|
||||||
|
36
tsp/linkedlist.py
Normal file
36
tsp/linkedlist.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class LinkedList(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.first_node = None
|
||||||
|
self.last_node = None
|
||||||
|
|
||||||
|
def new_node(self, prev_node=None, data=None, next_node=None):
|
||||||
|
return {
|
||||||
|
"prev_node": prev_node,
|
||||||
|
"data": data,
|
||||||
|
"next_node": next_node}
|
||||||
|
|
||||||
|
def append(self, data):
|
||||||
|
new_node = self.new_node(self.last_node, data, None)
|
||||||
|
if not self.first_node:
|
||||||
|
self.first_node = new_node
|
||||||
|
self.last_node = new_node
|
||||||
|
else:
|
||||||
|
self.last_node["next_node"] = new_node
|
||||||
|
self.last_node = new_node
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
def index(self, index):
|
||||||
|
for i, node in enumerate(self.iter()):
|
||||||
|
if i == index:
|
||||||
|
return node["data"]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.iter()
|
||||||
|
|
||||||
|
def iter(self):
|
||||||
|
current_node = self.first_node
|
||||||
|
while current_node:
|
||||||
|
yield current_node['data']
|
||||||
|
current_node = current_node['next_node']
|
249
tsp/tsp.py
249
tsp/tsp.py
@ -1,7 +1,7 @@
|
|||||||
import math
|
import math
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from geometry import do_intersect
|
from geometry import intersect
|
||||||
|
|
||||||
Point = namedtuple("Point", ['index', 'x', 'y'])
|
Point = namedtuple("Point", ['index', 'x', 'y'])
|
||||||
|
|
||||||
@ -9,33 +9,32 @@ Point = namedtuple("Point", ['index', 'x', 'y'])
|
|||||||
def parse_input_data(input_data):
|
def parse_input_data(input_data):
|
||||||
lines = input_data.split('\n')
|
lines = input_data.split('\n')
|
||||||
node_count = int(lines[0])
|
node_count = int(lines[0])
|
||||||
return [Point(i, *map(float, lines[i].split()))
|
return [Point(i, *map(float, lines[i + 1].split()))
|
||||||
for i in range(1, node_count + 1)]
|
for i in range(0, node_count)]
|
||||||
|
|
||||||
|
|
||||||
def plot_solution(solution, points):
|
def plot_graph(points):
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
def plot_arrows():
|
def plot_arrows():
|
||||||
for i in range(len(solution)):
|
for i in range(len(points)):
|
||||||
p_1_index = solution[i - 1]
|
p1 = points[i - 1]
|
||||||
p_2_index = solution[i]
|
p2 = points[i]
|
||||||
plot_arrow(p_1_index, p_2_index)
|
plot_arrow(p1, p2)
|
||||||
|
|
||||||
def plot_arrow(p_1_index, p_2_index):
|
def plot_arrow(p1, p2):
|
||||||
p_1 = points[p_1_index]
|
x = p1.x
|
||||||
p_2 = points[p_2_index]
|
y = p1.y
|
||||||
x = p_1.x
|
dx = p2.x - x
|
||||||
y = p_1.y
|
dy = p2.y - y
|
||||||
dx = p_2.x - x
|
opt = {'head_width': 0.4, 'head_length': 0.4, 'width': 0.05,
|
||||||
dy = p_2.y - y
|
'length_includes_head': True}
|
||||||
plt.arrow(x, y, dx, dy,
|
plt.arrow(x, y, dx, dy, **opt)
|
||||||
head_width=0.5, head_length=0.5)
|
|
||||||
|
|
||||||
def plot_points():
|
def plot_points():
|
||||||
for i, p in enumerate(points):
|
for p in points:
|
||||||
plt.plot(p.x, p.y, '')
|
plt.plot(p.x, p.y, '')
|
||||||
plt.text(p.x, p.y, ' ' + str(i))
|
plt.text(p.x, p.y, ' ' + str(p.index))
|
||||||
|
|
||||||
plot_points()
|
plot_points()
|
||||||
plot_arrows()
|
plot_arrows()
|
||||||
@ -45,78 +44,31 @@ def plot_solution(solution, points):
|
|||||||
def solve_it(input_data):
|
def solve_it(input_data):
|
||||||
|
|
||||||
@lru_cache(maxsize=1000000)
|
@lru_cache(maxsize=1000000)
|
||||||
def length(point_1_index, point_2_index):
|
def length(p_1, p_2):
|
||||||
p_1 = points[point_1_index]
|
|
||||||
p_2 = points[point_2_index]
|
|
||||||
return math.sqrt((p_1.x - p_2.x)**2 + (p_1.y - p_2.y)**2)
|
return math.sqrt((p_1.x - p_2.x)**2 + (p_1.y - p_2.y)**2)
|
||||||
|
|
||||||
def prepare_output_data(solution):
|
def prepare_output_data(points):
|
||||||
obj = calculate_length(solution)
|
obj = total_length(points)
|
||||||
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
|
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
|
||||||
output_data += ' '.join(map(str, solution))
|
output_data += ' '.join(map(lambda p: str(p.index), points))
|
||||||
return output_data
|
return output_data
|
||||||
|
|
||||||
def is_valid(solution):
|
def is_valid(points, num_nodes):
|
||||||
points = set(range(len(solution)))
|
expected_points = set(range(num_nodes))
|
||||||
assert(set(solution) == points)
|
assert(set([p.index for p in points]) == expected_points)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def calculate_length(solution):
|
def total_length(points):
|
||||||
obj = 0
|
return sum([length(points[i - 1], points[i])
|
||||||
for i in range(0, len(solution)):
|
for i in range(len(points))])
|
||||||
point_1_index = solution[i - 1]
|
|
||||||
point_2_index = solution[i]
|
|
||||||
obj += length(point_1_index, point_2_index)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def initial_solution_naiv():
|
def initial_solution_naiv():
|
||||||
return list(range(0, node_count))
|
return []
|
||||||
|
|
||||||
def does_edge_cause_intersection(edge, edges):
|
def reorder_points_greedy(points):
|
||||||
p1 = points[edge[0]]
|
current_point = points[0]
|
||||||
p2 = points[edge[1]]
|
solution = [current_point]
|
||||||
|
points = set(points[1:])
|
||||||
for existing_edge in edges:
|
|
||||||
q1 = points[existing_edge[0]]
|
|
||||||
q2 = points[existing_edge[1]]
|
|
||||||
print(p1, p2, q1, q2)
|
|
||||||
if do_intersect(p1, q1, p2, q2):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_dimensions(point_indices):
|
|
||||||
p = points[0]
|
|
||||||
x_min_p = p
|
|
||||||
x_max_p = p
|
|
||||||
y_min_p = p
|
|
||||||
y_max_p = p
|
|
||||||
|
|
||||||
x_min = p.x
|
|
||||||
x_max = p.x
|
|
||||||
y_min = p.y
|
|
||||||
y_max = p.y
|
|
||||||
|
|
||||||
for p in points:
|
|
||||||
if p.x < x_min:
|
|
||||||
x_min = p.x
|
|
||||||
x_min_p = p
|
|
||||||
if p.y < y_min:
|
|
||||||
y_min = p.y
|
|
||||||
y_min_p = p
|
|
||||||
if p.x > x_max:
|
|
||||||
x_max = p.x
|
|
||||||
x_max_p = p
|
|
||||||
if p.y > y_max:
|
|
||||||
y_max = p.y
|
|
||||||
y_max_p = p
|
|
||||||
|
|
||||||
return (x_min_p, x_max_p, y_min_p, y_max_p)
|
|
||||||
|
|
||||||
def initial_solution_greedy(point_indices):
|
|
||||||
current_point = get_dimensions(point_indices)[0].index
|
|
||||||
xs = [current_point]
|
|
||||||
points = set(point_indices)
|
|
||||||
points.remove(current_point)
|
|
||||||
|
|
||||||
while points:
|
while points:
|
||||||
min_length = 999999
|
min_length = 999999
|
||||||
@ -126,50 +78,101 @@ def solve_it(input_data):
|
|||||||
if new_length < min_length:
|
if new_length < min_length:
|
||||||
min_length = new_length
|
min_length = new_length
|
||||||
min_point = next_point
|
min_point = next_point
|
||||||
xs.append(min_point)
|
|
||||||
points.remove(min_point)
|
|
||||||
current_point = min_point
|
current_point = min_point
|
||||||
|
solution.append(current_point)
|
||||||
return xs
|
points.remove(current_point)
|
||||||
|
|
||||||
def local_search(solution):
|
|
||||||
# Find longest edges to swap
|
|
||||||
max_len_1 = 0
|
|
||||||
max_len_2 = 0
|
|
||||||
edge_1 = None
|
|
||||||
edge_2 = None
|
|
||||||
|
|
||||||
for i in range(node_count):
|
|
||||||
new_len = length(solution[i - 1], solution[i])
|
|
||||||
if new_len > max_len_1:
|
|
||||||
max_len_1 = new_len
|
|
||||||
edge_1 = (i - 1, i)
|
|
||||||
|
|
||||||
for i in range(node_count):
|
|
||||||
new_len = length(solution[i - 1], solution[i])
|
|
||||||
if new_len > max_len_2 and new_len != max_len_1:
|
|
||||||
max_len_2 = new_len
|
|
||||||
edge_2 = (i - 1, i)
|
|
||||||
|
|
||||||
a_1, a_2 = edge_1
|
|
||||||
print(edge_1, edge_2)
|
|
||||||
n_a_1, n_a_2 = [solution[a_1], solution[a_2]]
|
|
||||||
print(n_a_1, n_a_2)
|
|
||||||
b_1, b_2 = edge_2
|
|
||||||
n_b_1, n_b_2 = [solution[b_1], solution[b_2]]
|
|
||||||
print(n_b_1, n_b_2)
|
|
||||||
|
|
||||||
solution[a_2] = n_b_2
|
|
||||||
solution[b_2] = n_a_2
|
|
||||||
|
|
||||||
return solution
|
return solution
|
||||||
|
|
||||||
points = parse_input_data(input_data)
|
def swap_edges(i, j, points):
|
||||||
node_count = len(points)
|
_, p12 = points[i], points[i + 1]
|
||||||
solution = initial_solution_greedy(list(range(node_count)))
|
p21, _ = points[j], points[j + 1]
|
||||||
local_search(solution)
|
|
||||||
# solution = initial_solution_naiv()
|
points[i + 1] = p21
|
||||||
|
points[j] = p12
|
||||||
|
points[i + 2:j] = points[i + 2:j][::-1]
|
||||||
|
return points
|
||||||
|
|
||||||
|
def edge(p1, p2):
|
||||||
|
return "{} -> {}".format(p1.index, p2.index)
|
||||||
|
|
||||||
|
def local_search(points, ignore_list):
|
||||||
|
# Find longest edges to swap
|
||||||
|
#print("-" * 80)
|
||||||
|
#print("Local search")
|
||||||
|
#print("ignore_list", ignore_list)
|
||||||
|
|
||||||
|
max_len = 0
|
||||||
|
max_index = None
|
||||||
|
for i in range(len(points)):
|
||||||
|
if points[i - 1] in ignore_list:
|
||||||
|
continue
|
||||||
|
new_len = length(points[i - 1], points[i])
|
||||||
|
if new_len > max_len:
|
||||||
|
max_len = new_len
|
||||||
|
p_i = i - 1
|
||||||
|
p1 = points[p_i]
|
||||||
|
p2 = points[p_i + 1]
|
||||||
|
#print("Found max_len for ", edge(p1, p2))
|
||||||
|
|
||||||
|
current_length = total_length(points)
|
||||||
|
for p_j in range(len(points)):
|
||||||
|
if p_j in [p_i, p_i + 1, p_i + 2]:
|
||||||
|
continue
|
||||||
|
q1 = points[p_j - 1]
|
||||||
|
q2 = points[p_j]
|
||||||
|
|
||||||
|
new_points = list(points)
|
||||||
|
swap_edges(p_i, p_j - 1, new_points)
|
||||||
|
new_length = total_length(new_points)
|
||||||
|
if new_length < current_length:
|
||||||
|
#print("Swaping", edge(points[p_i], points[p_i + 1]),
|
||||||
|
# "and", edge(points[p_j - 1], points[p_j]))
|
||||||
|
#print("Test new_points", new_length, "smaller", current_length)
|
||||||
|
#print("Better, return new_points.")
|
||||||
|
ignore_list.clear()
|
||||||
|
return new_points
|
||||||
|
|
||||||
|
#if intersect(p1, p2, q1, q2):
|
||||||
|
# print(edge(p1, p2), "intersects", edge(q1, q2))
|
||||||
|
# new_points = list(points)
|
||||||
|
# swap_edges(p_i, p_j - 1, new_points)
|
||||||
|
# new_length = total_length(new_points)
|
||||||
|
# print("Test new_points", new_length, "smaller", current_length)
|
||||||
|
# if new_length < current_length:
|
||||||
|
# print("Better, return new_points.")
|
||||||
|
# ignore_list.append(p1)
|
||||||
|
# return new_points
|
||||||
|
# else:
|
||||||
|
# print("Worse, find better intersection.")
|
||||||
|
|
||||||
|
#print("Did not find an intersection that provides better results.")
|
||||||
|
ignore_list.append(p1)
|
||||||
|
return points
|
||||||
|
|
||||||
|
points = parse_input_data(input_data)
|
||||||
|
points = reorder_points_greedy(points)
|
||||||
|
num_nodes = len(points)
|
||||||
|
|
||||||
|
|
||||||
|
ignore_list = []
|
||||||
|
while True:
|
||||||
|
points_before_change = list(points)
|
||||||
|
try:
|
||||||
|
points = local_search(points, ignore_list)
|
||||||
|
except UnboundLocalError:
|
||||||
|
break
|
||||||
|
is_valid(points, num_nodes)
|
||||||
|
value = total_length(points)
|
||||||
|
|
||||||
|
# plot_graph(points_before_change)
|
||||||
|
is_valid(points, num_nodes)
|
||||||
|
return prepare_output_data(points)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
file_location = "data/tsp_200_2"
|
||||||
|
with open(file_location, 'r') as input_data_file:
|
||||||
|
input_data = input_data_file.read()
|
||||||
|
print(solve_it(input_data))
|
||||||
|
|
||||||
plot_solution(solution, points)
|
|
||||||
is_valid(solution)
|
|
||||||
return prepare_output_data(solution)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user