作者:mobiledu2502908023 | 来源:互联网 | 2023-10-13 23:46
简介
PSO(粒子群算法)是群智能算法的一种,其他的群智能算法还有蚁群算法,遗传算法等。其他的智能算法还有模拟退火。之前看过一段时间的PSO,商务智能课程最后的大作业便想用一下,刚好在github上看到有人用模拟退火解决TSP问题,而且效果不错,于是便萌生了利用PSO求解TSP问题的想法。
TSP问题想必大家再熟悉不过了,它是一个NP-hard问题,难以用暴力枚举的算法进行计算。针对NP-hard问题,有许多方法可以求得其近似解,而且效果不错,进化计算算法和群智能算法便是其中之一。
之前也在博客上看过其他人用PSO求解TSP问题,主要有一下几个问题 :
- TSP问题的规模相对较小
- 对于规模较大的问题,结果并不算太好。
一般而言,如果不考虑对TSP问题的额外的处理方式,直接用PSO算法,对于一些规模较小的问题(节点数量一般不超过20),PSO算法的效果还行,但是,当问题的规模变得比较大时(50及以上),PSO算法就无能为力。一个重要的原因在于当节点数量增加时,问题的复杂度会变得很大,PSO算法会陷入局部最优,而且这个局部最优往往离最优解很远。这个时候,有两种选择,一是改进PSO算法本身,这个基本很难,我尝试过,效果并不太好,PSO算法的模式基本都是固定的,如果只改变速度中的权重或是简单的调参,收效甚微;二是改变对TSP问题的编码以及粒子速度与位置的定义,使问题本身相对更加容易处理。我用的是第二种方法。
方法与过程
1.粒子的定义
1.位置
对于TSP问题,我们一般将粒子的位置定义为一个位置点的序列。比如{1,2,3,4,5},它表示的是一条路径,即1->2, 2->3, 3->4, 4->5, 5->1(我选的TSP问题是一个环路,所以最后的路径会成环),路径的长度可以计算每一条边的长度并求和得到,边的距离是两点之间的欧氏距离(问题中的点都是二维的)。
2.速度
TSP问题中粒子的速度是粒子在当前解空间的运动速度,一般是与粒子当前位置和粒子群体的历史最好位置的差成正比(有时还要考虑粒子当前位置与粒子的历史最好位置的差)。
在此问题中,粒子的速度被定义为一个二元组合,表示当前路径(即粒子的位置)的一个交换操作。比如(2,3),表示对当前粒子位置中节点2和3进行一次交换操作。粒子的速度可以与位置进行求和,例如,{1,2,3,4,5}+{(2,3)}={1,3,2,4,5}。{(1,2)}和{(2,1)}是两个相同的速度,交换不会考虑粒子的先后顺序。速度的长度定义为元组的个数。
3.运算规则
a. 位置-位置=速度
位置相减会得到一个速度,即一个元组序列。例如,{1,2,3,4,5}-{1,2,4,3,5}={(3,4)},我们可以这样看,两个速度的差别在于3,4两个节点的位置不同,只需将第二个速度进行一次节点的交换便可得到第一个速度。
b. 速度+速度=速度
两个速度相加会得到一个新的速度。比如{(1,2)}+{(3,4)}={(1,2),(3,4)}。如果有重复的元组,则会去掉相应的重复项(因为两个相同的交换操作会相互抵消)。{(1,2)}+{(2,1)}={(1,2)}。
c. 速度*常数=速度
速度乘以常数会得到一个速度,只不过这个速度的长度和新的速度长度不同。
speed_1*c=speed_2, 则{length(speed_1)*c]=length(speed_2), 左边是向下取整。例如,{(1,2),(4,5)}*0.5={(1,2)}。
d. 位置+速度=位置
位置和速度相加会得到一个新的位置。比如,{1,2,3,4,5}+{(1,2)}={2,1,3,4,5}。按照速度中的元组对位置进行相应变换即可。
e.滑动运算
滑动操作是对粒子的位置进行操作。假定速度X={x1,x2,x3,...xm}, SL(X,k)={x(1+k)%m, x(2+k)%m,....,x(m+k)%m}。这里定义这个运算的原因在于粒子速度的等价性。比如,{1,2,3,4,5}与{5,4,3,2,1},它们是两个相同的速度,但是表示不同,如果直接对其做差,相当于做了一次冒泡排序,但是事实上他们的差值应该为0。所以,我们在对速度进行做差时,会用速度的n-1个等义(相当于n-1次滑动运算的结果)表示进行做差,取其中长度最小的速度作为最后的结果。
f.交叉消除
这个运算可以说是这个问题的核心。我在用一般的PSO算法进行求解的时候,发现了一个问题,就是结果中的路径会有大量的交叉。
如上图所示,而且我在博客上看很多人利用PSO算法求解TSP问题的时候也有同样的问题。这是一个很重要的原因,因为大多数的解空间都会有大量的不好的解,这些不好的解的原因在于它们存在大量的边的交叉。于是,我们定义了消除交叉的操作。具体请看下图
这个算法不是简单的将两条边进行交换就完事了,我尝试过这种操作,效果很不好。它会将后面的边也进行交换。只改变了交叉的边的那一部分,对后面的操作没有影响。
结果分析
最后的结果如下图:
可以看到,与之前相比好了很多,而且与最优解非常接近,除了一些小的瑕疵。
下面是我参考的一些资料。
1.测试数据
2.参考论文