首页 理论教育 旅行商问题的求解指南

旅行商问题的求解指南

时间:2023-11-08 理论教育 版权反馈
【摘要】:尽管问题在计算上很困难,但已经有了大量的启发式算法和精确算法来求解数量上万的实例,并且能将误差控制在1%以内。早期的研究者使用精确算法求解旅行商问题,常用的算法包括分支定界法、线性规划法、动态规划法等。基于模拟退火算法的旅行商问题求解我们在前面的例子中曾经提过贪心算法。

旅行商问题的求解指南

旅行商问题描述

旅行商问题(Travelling Salesman Problem,TSP)是:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短?

旅行商问题

旅行商问题的可行解是所有顶点的全排列,随着顶点数的增加,旅行商问题会产生组合爆炸,它是一个NP完全问题,在运筹学和理论计算机科学中非常重要,在交通运输、电路板线路设计以及物流配送领域有着广泛的应用。最早的旅行商问题的数学规划是由Dantzig(1959年)等人提出的,并且他们在最优化领域中进行了深入研究。许多优化方法都用它作为一个测试基准。尽管问题在计算上很困难,但已经有了大量的启发式算法和精确算法来求解数量上万的实例,并且能将误差控制在1%以内。

早期的研究者使用精确算法求解旅行商问题,常用的算法包括分支定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法、模拟退火算法、蚁群算法、禁忌搜索算法、贪婪算法和神经网络等。

基于模拟退火算法的旅行商问题求解

我们在前面的例子中曾经提过贪心算法。如果把寻找最优值看作下山找到最低处的过程,贪心算法就是朝着海拔下降的方向行进,这有可能陷入一个并不是最低点的山谷(局部最优值),如下图所示。

算法搜索过程

模拟退火算法允许在搜索过程中,以一定的概率进入海拔更高的地方,这样反而有更多的可能性找到真正的低点。模拟退火算法来源于固体退火原理,将固体加到高温,然后徐徐冷却,加温时固体内部粒子随温度的升高变为无序状,内能增大,而冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。根据Metropolis准则,粒子在温度为T时趋于平衡的概率为e(-ΔE/(kT)),其中E为温度为T时的内能,ΔE为其改变量,k为玻尔兹曼(Boltzmann)常数。

如何将上述过程转成一个可以利用的算法?可以看到,在退火过程中,随着内能的减少,当量最少时,粒子趋于平衡,达到某个稳态。如果这个内能对应需要优化的目标函数值,且是求最小值,则当两个解对应的目标函数值的差很小时,可以说进入了某个谷底,为了避免进入局部的谷底,新解的接受与否并不仅取决于目标函数值的差,而是以一定的概率来确定。模拟退火算法的流程如下。

初始化:初始温度为T(充分大),温度下限为Tmin(充分小),初始解状态为S(是算法迭代的起点),每个T值的迭代次数都为L。

② 对k=1,…,L做第③至第⑥步。

③ 产生新解S′。

④ 计算增量ΔE=C(S′)-C(S),其中C(S)为评价函数。

⑤ 根据Metropolis准则确定是否接受新解:若ΔE<0则接受S′作为新的当前解,否则以概率exp(-ΔE/T)接受S′作为新的当前解。

⑥ 如果满足终止条件(例如连续若干个新解都没有被接受),则输出当前解作为最优解,结束程序。

⑦ T逐渐减小,且T>Tmin,然后转第②步。当T≤Tmin时,结束程序。

下面我们利用模拟退火算法对TSP问题进行求解,本程序参考https://ericphanson.com/blog/2016/the-traveling-salesman-and-10-lines-of-python/。

--------------------part 1导入相应的库--------------------

import random,numpy,math,copy,matplotlib.pyplot as plt

--------------------part 2 生成初始数据--------------------

#随机生成15个城市

cities=[random.sample(range(100),2)for x in range(15)];

#生成一个旅行路径tour,即城市号的组合

tour=random.sample(range(15),15);

------------------part 3调用模拟退火算法------------------

#生成参数temperature,取值范围为101~105,即[1,100 000],一共有100 000个数(www.xing528.com)

for temperature in numpy.logspace(0,5,num=100000)[::-1]:

#在15个数中任意选择两个位置

[i,j]=sorted(random.sample(range(15),2));

#对原有路径tour中的i,j位置(计数从0开始)的城市进行调换,形成新的路径

newTour=tour[:i]+tour[j:j+1]+tour[i+1:j]+tour[i:i+1]+tour[j+1:];

#计算旧路径中会改变部分的路径长度,即tour中i-1到i+1,j-1到j+1之间的距离

oldDistances=sum([math.sqrt(sum([(cities[tour[(k+1)%15]][d]-cities[tour[k % 15]][d])**2 for d in[0,1]]))for k in[j,j-1,i,i-1]])

#计算新路径中改变部分的路径长度

newDistances=sum([math.sqrt(sum([(cities[newTour[(k+1)% 15]][d]-cities[newTour[k % 15]][d])**2 for d in[0,1]]))

for k in[j,j-1,i,i-1]])

#计算模拟退火算法中的能量变化(此处为路径长度的变化),当大于某个随机概率时,接受新的路径,新路径长度越短,被接受的概率就越大

if math.exp((oldDistances-newDistances)/temperature)>random.random():

tour=copy.copy(newTour);

-----------------------part 4画图-----------------------

plt.plot([cities[tour[i % 15]][0]for i in range(16)],[cities[tour[i % 15]][1]for i in range(16)],'xb-');

plt.show()

下面对以上程序中的重要部分进行解读。

假定有15个城市,这里的目标是列出一个“城市”列表,每个城市都包含两个坐标。

用语句“cities=[random.sample(range(100),2)for x in range(15)]”生成15组城市的坐标,例如[[76,70],[7,94],[33,17],[34,45],[34,13],[62,2],[52,62],[19,58],[60,81],[34,30],[60,84],[95,65],[58,74],[34,15],[98,70]]。

再给这些城市进行编码,利用语句“tour=random.sample(range(15),15)”生成一个列表变量tour,包含15个城市的编号,例如[11,5,4,13,2,9,3,7,1,10,8,12,6,0,14]表示从城市11到城市5,以此类推。

如果要生成新的旅行路径newTour,这里采用在原有路径tour上进行城市置换。假如此时i=2,j=9,则新的路线“newTour=tour[:i]+tour[j:j+1]+tour[i+1:j]+tour[i:i+1]+tour[j+1:]”实际上是将旧路线中的第3和第10个城市进行了互换,如下图所示。

第3和第10个城市互换

因此,在计算路径差时,只需要计算上图圆圈处的路径差,其他部分都是一样的。这就是oldDistances和newDistances计算公式中所呈现的部分。

这样就可以得到优化后的路径,如下图所示。

优化路径结果

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈