Python 优化旅行商算法(时间旅行者算法)
我尝试优化一个简单的python算法,该算法大致解决了旅行推销员问题:Python 优化旅行商算法(时间旅行者算法),python,algorithm,python-3.6,traveling-salesman,Python,Algorithm,Python 3.6,Traveling Salesman,我尝试优化一个简单的python算法,该算法大致解决了旅行推销员问题: import math import random import matplotlib.pyplot as plt import datetime #Distance between two point def distance(point1, point2): return math.sqrt((point2[0]-point1[0])**2+(point2[1]-point1[1])**2) #TSP Ti
import math
import random
import matplotlib.pyplot as plt
import datetime
#Distance between two point
def distance(point1, point2):
return math.sqrt((point2[0]-point1[0])**2+(point2[1]-point1[1])**2)
#TSP TimeTraveler Algorithm
def TSP_TimeTraveler(Set_Points):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points
points = Set_Points.copy()
route = []
#Take 3 points at random
route.append(points.pop(random.randint(0,len(points)-1)))
route.insert(0,points.pop(random.randint(0,len(points)-1)))
route.insert(1,points.pop(random.randint(0,len(points)-1)))
#Calulating the initial route length
Length = distance(route[0],route[1]) + distance(route[1],route[-1]) + distance(route[-1],route[0])
#Time Traveler Algorithm
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
#Take a random point from the Set
point = points.pop(random.randint(0,len(points)-1))
###############################################################################################################
#### Finding the closest route segment by calculation all lengths posibilities and finding the minimum one ####
###############################################################################################################
Set_Lengths = []
for i in range(1,len(route)):
#Set of Lengths when the point is on each route segment except the last one
L = Length - distance(route[i-1],route[i]) + distance(route[i-1],point) + distance(point, route[i])
Set_Lengths.append((i,L))
#Adding the last length when the point is on the last segement
L = Length - distance(route[-1],route[0]) + distance(route[-1],point) + distance(point, route[0])
Set_Lengths.append((0,L))
###############################################################################################################
###############################################################################################################
#Sorting the set of lengths
Set_Lengths.sort(key=lambda k: k[1])
#Inserting the point on the minimum length segment
route.insert(Set_Lengths[0][0], point)
#Updating the new route length
Length = Set_Lengths[0][1]
#Connecting the start point with the finish point
route.append(route[0])
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
return route
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 2520
#Generating a set of random 2D points
points = []
for i in range(size):
points.append([random.uniform(0, 100),random.uniform(0, 100)])
#Solve TSP
route = TSP_TimeTraveler(points)
#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
该算法分为3个简单步骤:
1/第一步,我从点集中随机选取3个点,并将其连接为初始路线
2/然后每下一步,我从左边的一组点中随机抽取一个点。然后试着找到我拥有的路线中最近的一段,并将其连接到它
3/我不断重复步骤2/直到剩下的点集为空
下面是算法如何求解120个点集的gif:
我给它取名为“时间旅行者”,因为它的操作就像一个贪婪的推销员算法。但是,贪婪的推销员不是现在去最近的新城旅行,而是回到过去去他已经去过的最近的城市,然后去参观那座新城,然后继续他的正常路线
时间旅行者从三个城市开始旅行,旅行者在过去的每一步都会增加一个新的城市,直到他到达一个现在,在那里他访问了所有的城市并回到了他的家乡城市
该算法可以快速地为小的点集提供合适的解。以下是每一组的执行时间,它们都是在2.6GHz双核Intel core i5处理器Macbook上完成的:
- 大约0.03秒内120点
- 在大约0.23秒内获得360点积分
- 10秒钟内获得2520分
- 大约3分钟内获得10000点积分
- 大约5小时内获得100000分()
PS:我的算法和代码是开放的。来自互联网的人们,可以在任何项目或任何研究中自由使用它。谢谢您的评论。我使用对象、集合和链表重新实现了算法。我还从距离函数中去掉了平方根。现在代码看起来更干净了:
import math
import random
import datetime
import matplotlib.pyplot as plt
#Distance between two point
def distance(point1, point2):
return (point2[0]-point1[0])**2 + (point2[1]-point1[1])**2
#Distance between two point
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
class TSP_TimeTraveler():
def __init__(self, dataval=None):
self.count = 0
self.position = None
self.length = 0
def get_position():
return self.position
def next_city():
self.position = self.position.nextval
return self.position
#adding a city to the current route with Time Traveler Algorithm :
def add_city(self, point):
node = Node(point)
if self.count <=0 :
self.position = node
elif self.count == 1 :
node.nextval = self.position
self.position.nextval = node
self.length = 2*distance(self.position.dataval,node.dataval)
else :
#Creating the traveler
traveler = self.position
c = traveler.dataval #current position
n = traveler.nextval.dataval #next position
#Calculating the length of adding the city to the path
Min_L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)
Min_Node = traveler
traveler = traveler.nextval
while traveler != self.position :
c = traveler.dataval #current position
n = traveler.nextval.dataval #next position
#Calculating the length of adding the city to the path
L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)
#Searching the path to the of city with minimum length
if L < Min_L :
Min_L = L
Min_Node = traveler
traveler = traveler.nextval
#Adding the city to the minimum path
node.nextval = Min_Node.nextval
Min_Node.nextval = node
self.length = Min_L
#Incrementing the number of city in the route
self.count = self.count + 1
#Get the list of the route
def getRoute(self):
result = []
traveler = self.position
result.append(traveler.dataval)
traveler = traveler.nextval
while traveler != self.position :
result.append(traveler.dataval)
traveler = traveler.nextval
result.append(traveler.dataval)
return result
def Solve(self, Set_points):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points list
points = Set_points.copy()
#Transform the list into set
points = set(tuple(i) for i in points)
#Add
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
point = points.pop()
self.add_city(point)
result = self.getRoute()
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
return result
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 120
#Generating a set of random 2D points
points = []
for i in range(size):
points.append((random.uniform(0, 100),random.uniform(0, 100)))
#Solve TSP
TSP = TSP_TimeTraveler()
route = TSP.Solve(points)
#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
导入数学
随机输入
导入日期时间
将matplotlib.pyplot作为plt导入
#两点之间的距离
def距离(点1、点2):
返回(点2[0]-点1[0])**2+(点2[1]-点1[1])**2
#两点之间的距离
类节点:
定义初始化(self,dataval=None):
self.dataval=dataval
self.nextval=None
类TSP_TimeTraveler():
定义初始化(self,dataval=None):
self.count=0
self.position=无
self.length=0
def get_位置():
返回自我位置
def next_city():
self.position=self.position.nextval
返回自我位置
#使用时间旅行者算法将城市添加到当前路线:
def添加城市(自身,点):
节点=节点(点)
如果self.count为0:
打印(“左点:”,len(点),“”,end=“\r”)
point=points.pop()
自我添加城市(点)
结果=self.getRoute()
#用于计算执行时间
time\u end=datetime.datetime.now()
增量=(时间结束时间开始)。总秒数()
打印(“左点:”,len(点),“完成”,)
打印(“执行时间:,增量,秒”)
返回结果
#######################
#测试算法#
#######################
#集合的大小
尺寸=120
#生成一组随机二维点
点数=[]
对于范围内的i(尺寸):
points.append((random.uniform(01000),random.uniform(01000)))
#求解TSP
TSP=TSP\u时间旅行者()
路线=TSP。求解(点)
#绘制解决方案
plt.散射(*压缩(*点),s=5)
plt.地块(*zip(*路线))
plt.轴(“缩放”)
plt.show()
使用PyPy而不是普通的python,它的运行速度要快得多:
- 120英寸,约0.03秒
- 360英寸,约0.05秒
- 约0.22秒后返回2520
- 大约2秒内有10000个
- 大约7分钟10万
接下来,我将尝试使用双链表和KD树实现一个2-opt。因此,它可以解决无交叉的大型集合。我改进了算法,在每次插入时添加了双链表和2-opt:
import math
import random
import datetime
import matplotlib.pyplot as plt
#Distance between two point
def distance(point1, point2):
return (point2[0]-point1[0])**2 + (point2[1]-point1[1])**2
#Intersection between two segments
def intersects(p1, q1, p2, q2):
def on_segment(p, q, r):
if r[0] <= max(p[0], q[0]) and r[0] >= min(p[0], q[0]) and r[1] <= max(p[1], q[1]) and r[1] >= min(p[1], q[1]):
return True
return False
def orientation(p, q, r):
val = ((q[1] - p[1]) * (r[0] - q[0])) - ((q[0] - p[0]) * (r[1] - q[1]))
if val == 0 : return 0
return 1 if val > 0 else -1
o1 = orientation(p1, q1, p2)
o2 = orientation(p1, q1, q2)
o3 = orientation(p2, q2, p1)
o4 = orientation(p2, q2, q1)
if o1 != o2 and o3 != o4:
return True
if o1 == 0 and on_segment(p1, q1, p2) : return True
if o2 == 0 and on_segment(p1, q1, q2) : return True
if o3 == 0 and on_segment(p2, q2, p1) : return True
if o4 == 0 and on_segment(p2, q2, q1) : return True
return False
#Distance Double Linked Node
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.prevval = None
self.nextval = None
class TSP_TimeTraveler():
def __init__(self):
self.count = 0
self.position = None
self.length = 0
self.traveler = None
self.travelert_past = None
self.is_2opt = True
def get_position():
return self.position
def traveler_init(self):
self.traveler = self.position
self.travelert_past = self.position.prevval
return self.traveler
def traveler_next(self):
if self.traveler.nextval != self.travelert_past:
self.travelert_past = self.traveler
self.traveler = self.traveler.nextval
return self.traveler, False
else :
self.travelert_past = self.traveler
self.traveler = self.traveler.prevval
return self.traveler, True
#adding a city to the current route with Time Traveler Algorithm :
def add_city(self, point):
node = Node(point)
if self.count <=0 :
self.position = node
elif self.count == 1 :
node.nextval = self.position
node.prevval = node
self.position.nextval = node
self.position.prevval = self.position
self.length = 2*distance(self.position.dataval,node.dataval)
elif self.count == 2 :
node.nextval = self.position.nextval
node.prevval = self.position
self.position.nextval.prevval = node
self.position.nextval = node
self.length = 2*distance(self.position.dataval,node.dataval)
else :
#Creating the traveler
traveler = self.traveler_init()
c = traveler #current position
prev = False #inverse link
n, prev = self.traveler_next()
#Calculating the length of adding the city to the path
Min_prev = prev
Min_L = self.length-distance(c.dataval,n.dataval)+distance(c.dataval,node.dataval)+distance(node.dataval,n.dataval)
Min_Node = c
traveler = n
while traveler != self.position :
c = n #current position
n, prev = self.traveler_next()
#Calculating the length of adding the city to the path
L = self.length-distance(c.dataval,n.dataval)+distance(c.dataval,node.dataval)+distance(node.dataval,n.dataval)
#Searching the path to the of city with minimum length
if L < Min_L :
Min_prev = prev
Min_L = L
Min_Node = c
traveler = n
if Min_prev :
Min_Next_Node = Min_Node.prevval
else :
Min_Next_Node = Min_Node.nextval
node.nextval = Min_Next_Node
node.prevval = Min_Node
if Min_prev :
Min_Node.prevval = node
else :
Min_Node.nextval = node
if Min_Next_Node.nextval == Min_Node:
Min_Next_Node.nextval = node
else :
Min_Next_Node.prevval = node
self.length = Min_L
#2-OP
if self.is_2opt == True :
self._2opt(Min_Node, node, Min_Next_Node)
#Incrementing the number of city in the route
self.count = self.count + 1
#apply the 2opt to a-b-c
def _2opt(self, a, b, c):
traveler = self.traveler_init()
c1 = a
c2 = b
n1 = b
n2 = c
c = traveler #current position
t_prev = False
n, t_prev = self.traveler_next()
traveler = n
while traveler != self.position :
cross = False
if (c.dataval != c1.dataval and c.dataval != c2.dataval and n.dataval != c1.dataval and n.dataval != c2.dataval) and intersects(c.dataval, n.dataval, c1.dataval, c2.dataval):
self._2optswap(c,n,c1,c2)
cross = True
a = n
n = c1
c2 = a
if (c.dataval != n1.dataval and c.dataval != n2.dataval and n.dataval != n1.dataval and n.dataval != n2.dataval) and intersects(c.dataval, n.dataval, n1.dataval, n2.dataval):
self._2optswap(c,n,n1,n2)
cross = True
a = n
n = n1
n2 = a
if cross:
return
c = n #current position
n, t_prev = self.traveler_next()
traveler = n
#swap between the crossed segment a-b and c-d
def _2optswap(self, a, b, c, d):
if a.nextval == b :
a.nextval = c
else :
a.prevval = c
if b.prevval == a :
b.prevval = d
else :
b.nextval = d
if c.nextval == d :
c.nextval = a
else :
c.prevval = a
if d.prevval == c :
d.prevval = b
else :
d.nextval = b
self.length = self.length - distance(a.dataval,b.dataval) - distance(c.dataval,d.dataval) + distance(a.dataval,c.dataval) + distance(b.dataval,d.dataval)
#Get the list of the route
def getRoute(self):
result = []
traveler = self.traveler_init()
result.append(traveler.dataval)
traveler, prev = self.traveler_next()
while traveler != self.position :
result.append(traveler.dataval)
traveler, prev = self.traveler_next()
result.append(traveler.dataval)
return result
def Solve(self, Set_points, with_2opt = True):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points list
points = Set_points.copy()
#Transform the list into set
points = set(tuple(i) for i in points)
#Add
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
point = points.pop()
self.add_city(point)
result = self.getRoute()
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
L=0
for i in range(len(result)-1):
L = L + math.sqrt((result[i-1][0]-result[i][0])**2 + (result[i-1][1]-result[i][1])**2)
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
print("Average time per point : ", 1000*delta/len(Set_points), "msecs")
print("Length : ", L)
return result
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 1000
#Generating a set of random 2D points
points = []
for i in range(size):
points.append((random.uniform(0, 100),random.uniform(0, 100)))
#Solve TSP
TSP = TSP_TimeTraveler()
route = TSP.Solve(points, with_2opt = True)
plt.scatter(*zip(*route), s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
导入数学
随机输入
导入日期时间
将matplotlib.pyplot作为plt导入
#两点之间的距离
def距离(点1、点2):
返回(点2[0]-点1[0])**2+(点2[1]-点1[1])**2
#两段的交点
def相交(p1、q1、p2、q2):
_段上的def(p、q、r):
如果r[0]=min(p[0],q[0])和r[1]=min(p[1],q[1]):
返回真值
返回错误
def方向(p、q、r):
val=((q[1]-p[1])*(r[0]-q[0])-((q[0]-p[0])*(r[1]-q[1]))
如果val==0:返回0
如果val>0,则返回1,否则返回-1
o1=方向(p1、q1、p2)
o2=方向(p1、q1、q2)
o3=方向(p2、q2、p1)
o4=方向(p2、q2、q1)
如果o1!=o2和o3!=o4:
返回真值
如果o1==0且在_段(p1、q1、p2)上:返回True
如果o2==0且在_段(p1、q1、q2)上:返回真值
如果o3==0且在_段(p2、q2、p1)上:返回真值
如果o4==0且在_段(p2、q2、q1)上:返回True
返回错误
#距离双链接节点
类节点:
定义初始化(self,dataval=None):
self.dataval=dataval
self.prevval=None
self.nextval=None
类TSP_TimeTraveler():
定义初始化(自):
self.count=0
self.position=无
self.length=0
self.traveler=无
self.travelert_pass=无
self.is_2opt=True
def get_位置():
返回自我位置
def traveler_init(自):
self.traveler=self.position
自助旅行者