This commit is contained in:
Felix Martin 2019-12-22 21:03:24 -05:00
parent 233c763805
commit 7481ee3f0a
3 changed files with 250 additions and 38 deletions

74
tsp/geometry.py Normal file
View File

@ -0,0 +1,74 @@
def on_segment(p, q, r):
# Given three colinear points p, q, r, the function checks if
# point q lies on line segment 'pr'
if q.x <= max(p.x, r.x) and q.x >= min(p.x, r.x) and \
q.y <= max(p.y, r.y) and q.y >= min(p.y, r.y):
return True
return False
def orientation(p, q, r):
# To find orientation of ordered triplet (p, q, r).
# The function returns following values
# 0 --> p, q and r are colinear
# 1 --> Clockwise
# 2 --> Counterclockwise
# See https://www.geeksforgeeks.org/orientation-3-ordered-points/
# for details of below formula.
val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
if val == 0:
return 0 # colinear
# clock or counterclock wise
if val > 0:
return 1
return 2
def in_square(p1, x_min, x_max, y_min, y_max):
if p1.x < x_min or p1.x > x_max:
return False
if p1.y < y_min or p1.y > y_max:
return False
return True
def in_circle(p1, p_center, radius):
return (p1.x - p_center.x)**2 + (p1.y - p_center.y)**2 < radius**2
def do_intersect(p1, q1, p2, q2):
# The main function that returns true if line segment 'p1q1'
# and 'p2q2' intersect.
# Find the four orientations needed for general and
# special cases
o1 = orientation(p1, q1, p2)
o2 = orientation(p1, q1, q2)
o3 = orientation(p2, q2, p1)
o4 = orientation(p2, q2, q1)
# General case
if (o1 != o2 and o3 != o4):
return True
# Special Cases
# p1, q1 and p2 are colinear and p2 lies on segment p1q1
if (o1 == 0 and on_segment(p1, p2, q1)):
return True
# p1, q1 and q2 are colinear and q2 lies on segment p1q1
if (o2 == 0 and on_segment(p1, q2, q1)):
return True
# p2, q2 and p1 are colinear and p1 lies on segment p2q2
if (o3 == 0 and on_segment(p2, p1, q2)):
return True
# p2, q2 and q1 are colinear and q1 lies on segment p2q2
if (o4 == 0 and on_segment(p2, q1, q2)):
return True
return False

View File

@ -1,45 +1,8 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import math
from collections import namedtuple
Point = namedtuple("Point", ['x', 'y'])
def length(point1, point2):
return math.sqrt((point1.x - point2.x)**2 + (point1.y - point2.y)**2)
def solve_it(input_data):
# Modify this code to run your optimization algorithm
# parse the input
lines = input_data.split('\n')
nodeCount = int(lines[0])
points = []
for i in range(1, nodeCount+1):
line = lines[i]
parts = line.split()
points.append(Point(float(parts[0]), float(parts[1])))
# build a trivial solution
# visit the nodes in the order they appear in the file
solution = range(0, nodeCount)
# calculate the length of the tour
obj = length(points[solution[-1]], points[solution[0]])
for index in range(0, nodeCount-1):
obj += length(points[solution[index]], points[solution[index+1]])
# prepare the solution in the specified output format
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
output_data += ' '.join(map(str, solution))
return output_data
import sys import sys
from tsp import solve_it
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys

175
tsp/tsp.py Normal file
View File

@ -0,0 +1,175 @@
import math
from functools import lru_cache
from collections import namedtuple
from geometry import do_intersect
Point = namedtuple("Point", ['index', 'x', 'y'])
def parse_input_data(input_data):
lines = input_data.split('\n')
node_count = int(lines[0])
return [Point(i, *map(float, lines[i].split()))
for i in range(1, node_count + 1)]
def plot_solution(solution, points):
import matplotlib.pyplot as plt
def plot_arrows():
for i in range(len(solution)):
p_1_index = solution[i - 1]
p_2_index = solution[i]
plot_arrow(p_1_index, p_2_index)
def plot_arrow(p_1_index, p_2_index):
p_1 = points[p_1_index]
p_2 = points[p_2_index]
x = p_1.x
y = p_1.y
dx = p_2.x - x
dy = p_2.y - y
plt.arrow(x, y, dx, dy,
head_width=0.5, head_length=0.5)
def plot_points():
for i, p in enumerate(points):
plt.plot(p.x, p.y, '')
plt.text(p.x, p.y, ' ' + str(i))
plot_points()
plot_arrows()
plt.show()
def solve_it(input_data):
@lru_cache(maxsize=1000000)
def length(point_1_index, point_2_index):
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)
def prepare_output_data(solution):
obj = calculate_length(solution)
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
output_data += ' '.join(map(str, solution))
return output_data
def is_valid(solution):
points = set(range(len(solution)))
assert(set(solution) == points)
return True
def calculate_length(solution):
obj = 0
for i in range(0, len(solution)):
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():
return list(range(0, node_count))
def does_edge_cause_intersection(edge, edges):
p1 = points[edge[0]]
p2 = points[edge[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:
min_length = 999999
min_point = None
for next_point in points:
new_length = length(current_point, next_point)
if new_length < min_length:
min_length = new_length
min_point = next_point
xs.append(min_point)
points.remove(min_point)
current_point = min_point
return xs
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
points = parse_input_data(input_data)
node_count = len(points)
solution = initial_solution_greedy(list(range(node_count)))
local_search(solution)
# solution = initial_solution_naiv()
plot_solution(solution, points)
is_valid(solution)
return prepare_output_data(solution)