改良圈算法详解

改良圈算法详解

对于改良圈算法,我们通常不会单独用它来解决某些问题的,由于改良圈算法搜索不够随机,所以通过它得到的几乎不是全局最优解,很容易陷入局部最优然后就停止搜索了。但是用它却可以得到比较好的初始解,得到比较好的初始解后我们可以用其它的算法做进一步计算,比如遗传算法。
改良圈法主要针对的应该是旅行商问题,其实和巡航问题都是差不多的。抽象成图论问题就是哈密顿图(Hamiltonian path或Traceable path).简单来说,就是图上有很多个点,我们从起点出发,需要依次经过所有的点,并且每个点只经过一次,最终回到起点。这个闭合的路径叫做哈密顿回路。对于一个含有n个节点(不包括原点)这样的路径个数是非常多的(n!个),每个路径对应的路径长度也是不同的,我们需要找到较短的路径。这时便需要用改良圈算法求比较好的初始解(路径)。

算法原理
兔兔在这里将通过一个例子进行讲解。对于巡航问题,首先生成20个位点,起点是在原点(0,0)处。

import numpy as np
x=np.random.randint(1,50,20)
y=np.random.randint(1,50,20)
coord=list(zip(x,y) #每个点对应坐标

结果如下

x=[89 72 15 49 84 35 37 52 40 21 67 96 60 51 57 45 84 51 55 44]
y=[58 11 77 20 32 76 79 24 33 77 68  3 46 84 63 79 92 35 86 74]
coord=[(89, 58), (72, 11), (15, 77), (49, 20), (84, 32), (35, 76), (37, 79), (52, 24), (40, 33), (21, 77), (67, 68), (96, 3), (60, 46), (51, 84), (57, 63), (45, 79), (84, 92), (51, 35), (55, 86), (44, 74)]

对应的图像如图1所示:
图1.绿色的是巡航点,红色的是起点,也是终点
对于任意两点的距离我们可以用欧氏距离来表示,即平面两点的直线距离,。

def distance(coord1,coord2):
	'''计算coord1与coord2两点坐标的距离'''
	return np.sqrt((coord1[0]-coord2[0])**2+(coord1[1]-coord2[1])**2)

我们再把平面这21个点用序号做好标签。原点是0,第一个点(89,58)是1,第二个点(72,11)是2 …以此类推。别忘了最后一个点也是原点0,标签是0.那么任意两个点i,j(i,j属于0~21)之间的距离可以用Dij表示。比如D23表示2点(72,11)和3点坐标(15,77)之间的距离。
那么,如果一个巡航路线确定,这个路线的长度也就确定了。比如一个巡航路线[0,1,2,3,4,5,6,7,8,9…18,19,20,0],它的路径长度就是D01+D12+…+D19 20+D20 0。
如果我们用穷举法把所有路径都列举出来,再找这些路径中的最短巡航路线,可以发现种类是20的全排列,那计算量也实在太大了。所以我们才用到一系列算法来求解。
改良圈的思路是:任意选路线中的两点,把两点中所有位置颠倒过来(即倒序),看倒序后的路线长度是否比之前短,如果变短就接受这个巡航路线,否则不接受,进行下一次选两点并进行倒序的操作。
例如,对于路线[0,1,3,2,6,4,5,0],我们选位置3和位置6交换顺序,巡航路线就是[0,4,6,2,3,5,0]。
改良圈算法的顺序是:先选位置2和3交换位置再判断,然后位置2和4,位置2和5,一直到2和倒数第3个。之后是3和4,3和5…3和倒数第3个。最终是倒数第4 个和倒数第3个.所以需要两个循环嵌套,这样能够把任意两个点进行比较。
比较也是有技巧的。我们不用把全部路径长度算出来的。对于路径[0,…u-1,u,u+1…v-1,v,v+1…0],交换u.v之间顺序后就是[0,…u-1, v, v-1,…,u+1,u,v+1],用距离表示两个式子,由于Dij=Dji,两式相减大部分都抵消了,最终交换后路径长度减去交换前路径长度∆D=(Du-1,v+Du,v+1)-(Du-1,u+Dv,v+1)。值为负数接受新解,否则不接受。

算法实现:

def distance(coord1,coord2):
		'''距离函数,求两点距离'''
    return np.sqrt((coord1[0]-coord2[0])**2+(coord1[1]-coord2[1])**2)
for m in np.arange(1,len(coord)-3):
    for n in np.arange(m+1,len(coord)-2):
        if distance(coord[m],coord[n])+distance(coord[m+1],coord[n+1])-distance(coord[m],coord[m+1])-distance(coord[n],coord[n+1]):
            coord[m+1:n+1]=coord[n:m:-1] #当距离变短时交换位置
        else:
            continue

最终我们测试以下结果

x=[0,89 ,72 ,15 ,49 ,84 ,35 ,37 ,52 ,40 ,21 ,67 ,96 ,60 ,51, 57 ,45, 84, 51 ,55 ,44,0]
y=[0,58, 11 ,77 ,20, 32, 76, 79, 24 ,33 ,77 ,68 , 3, 46 ,84 ,63, 79 ,92, 35 ,86, 74,0]
coord=list(zip(x,y)) #以上是初始数据
plt.plot(x,y) #未处理前的巡航路线[0,1,2....20,0]
plt.show()
#运行改良圈代码后,coord顺序变化
X=[];Y=[]
for xnew,ynew in coord:
    X.append(xnew)
    Y.append(ynew)
plt.plot(X,Y) #改良圈算法后的巡航路线
plt.show()

处理前的巡航路线
图2.处理前的巡航路线

处理后的巡航路线
图3.处理后的巡航路线
具体路线长度也是可以计算的。兔兔在这里就不计算了。比较两个图我们发现处理后的图看起来稍稍好看点,但仍是比较杂乱。这也说明了改良圈算法并不能得到最优解的。所以之后进一步的计算就显得尤为重要。
之后兔兔就以这个算法为基础,进一步讲解如何用遗传算法求解巡航问题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生信小兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值