【发布时间】:2021-01-19 20:02:20
【问题描述】:
我尝试优化一个简单的 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 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 图像:TimeTravelerAlgorithm.gif
我将其命名为“时间旅行者”,因为它的运作方式类似于贪婪的推销员算法。但是,贪婪的推销员不是前往现在最近的新城市,而是穿越到过去他已经去过的最近的城市,然后去参观那个新城市,然后继续他的正常路线。
时间旅行者开始了3个城市的路线,旅行者在他的过去每一步都添加一个新城市,直到他到达一个他访问了所有城市并返回他的家乡的现在。
该算法可以快速为少量点提供体面的解决方案。以下是每组数的执行时间,均在 2.6GHz 双核 Intel Core i5 处理器 Macbook 上制作:
- 大约 0.03 秒内 120 个点
- 360 点大约在 0.23 秒内
- 大约 10 秒内 2520 个点
- 3分钟左右10000分
- 大约 5 小时内获得 100 000 个积分 (Solution Map)
该算法远未得到优化,因为在某些情况下它会给出次优的交叉路由。这一切都是用纯python制作的。也许使用 numpy 或一些高级库甚至 GPU 可以加速程序。
我希望得到您的评论并帮助我们优化它。对于可能非常大的点集(从 100 万到 1000 亿点),我尝试在没有交叉路径的情况下近似求解。
PS:我的算法和代码是开放的。来自互联网的人,请随时在您拥有的任何项目或任何研究中使用它。
【问题讨论】:
-
您可以对距离平方进行比较,避免计算 sqrt()。
-
那里有一个有点相似的算法,但我不记得它叫什么了。性能优化将使用一种方法,使您可以在
O(log h)而不是O(h)中确定数据中最近的点,其中h是解决方案中的当前点。可能是KD树或其他东西。还实施 2- 或 3-opt 以摆脱十字架。 -
算法的时间复杂度是多少,实现又是怎样的?如果您的实现速度较慢,那么您可能会遇到数据结构问题以及您对它们的操作时间复杂度的假设(例如,插入列表是
O(n),来自 wiki.python.org/moin/TimeComplexity)。 -
如果您还不了解它们,我建议您针对“标准”TSP 基准测试数据案例运行您的算法,看看它们是如何做的。 math.uwaterloo.ca/tsp/data/index.html(这里是一个带有 TSPLIB 格式定义的 PDF comopt.ifi.uni-heidelberg.de/software/TSPLIB95/tsp95.pdf)
-
嗯,试试math.uwaterloo.ca/tsp/world/zitour.html。 VLSI 难以优化,因为它们在某些情况下可能是等距的(它们基本上是电路板),因此如果您不处理 AB 与 BC 距离相等的情况,您可能会在该点集上遇到问题。建议在其他基准实例上也对其进行测试。看看你哪里出错了。我可以看到算法是如何出错的,但 16% 的近似值还不错。如果您将 2-opt 提升为 3-opt,可能会有所帮助。我会实施并看看它是否有很大帮助。
标签: python algorithm python-3.6 traveling-salesman