回到主页

Local search之爬山算法(TSP)

——以解决TSP问题为例

旅行商问题,即TSP问题(Traveling Salesman Problem),经典组合优化问题之一,且是一个NP-Hard问题,该问题描述如下: 

假设有若干个城市,且任何两个城市之间的距离是确定的,现有一个旅行商人要拜访这些城市,要求旅行商从某城市出发,必须经过每个城市,且每个城市只能拜访一次,最后要回到出发的城市,问如何确定一条路线使路径长度最短。 

如下图所示: 

broken image

一、LS算法的介绍 

1.1 What is Local Search?

局部搜索(Local Search)是解决最优化问题的一种启发式算法(heuristic algorithm),同时也是一种简单的贪心搜索算法(Greedy Search algorithms)。该算法每次从当前解的邻域解空间中选择一个邻居作为下次迭代的当前解,如有更优的解则移动至该解并继续执行搜索,否则就停止算法,直到达到一个局部最优解(local optimal solution)。

1.2 邻域及邻域算子的概念 

1.2.1 什么是Neighborhood)? 

在距离空间中,如果我们将搜索空间视作如下图所示的矩形S,x是其中一点,以该点为圆心、特定长度为半径画圆,得到的圆N(x)即为点x的邻域。其中,N(x)是一系列点的集合,并且集合内的所有点在某些可测度的特性上与已知点x高度相似。 

broken image

 在组合优化问题中,邻域一般定义为由给定转化规则对给定的问题域上每结点进行转化所得到的问题域上结点的集合。如《现代优化计算方法(第2版)》中对“邻域”的定义如下[1]: 

broken image

 1.2.2 什么是邻域算子(neighbor operator)? 

邻域算子是一个函数,通过这个函数,能产生当前解s对应的邻居解集合。例如:对于一个bool型问题,其当前解为:s=1001,当将邻域算子定义为翻转其中一个bit时,得到的邻居解的集合N(s)={0001,1101,1011,1000},其中N(s) ∈ S。同理,当将邻域动作定义为互换相邻bit时,得到的邻居解的集合N(s)={0101,1001,1010}。常见的邻域算子包括:swap、2-opt、insret、1-flit等。 

1.3 局部搜索的一般过程 

broken image

如上图所示,局部搜索会先从一个初始解(s0)开始,通过邻域算子,产生初始解的邻居解,然后根据某种策略选择邻居解(s0->s1->s2)。一直重复以上过程,直到达到终止条件,获得局部最优解(s3)。值得注意的是,不同局部搜索算法的区别就在于邻域算子的定义以及选择邻居解的策略,这也是决定算法好坏的关键之处。 

局部搜索的过程可用一段伪代码表示: 

broken image

伪代码的文字表述如下: 

步骤1:产生一个初始解s0 

步骤2:当未达到终止条件时,生成邻域N(s) 

步骤3:从N(s)中挑选优于s0的解s’,令s=s’

步骤4:当N(s)中没有更优的邻居解时,停止搜索,转步骤5,否则回到步骤2 

步骤5:输出局部最优解s’ 

1.4 Why need local search?

对于某些计算起来非常复杂的最优化问题,比如各种NP hard问题,找到最优解所需要的时间会随问题规模呈指数增长,因此现实情境中更多地采用各种近似算法(Approximate algorithms)在可接受的时间范围内寻找次优解或近似最优解,局部搜索便是诸多近似算法中的一种。它由爬山算法改进而来,是一种比较通用的优化算法,可以较为方便地应用于具体的优化问题。而且局部搜索算法的运行时间和优化程度可以由用户通过参数来控制,有着简单、灵活及易于实现的优点。 

下面将以局部搜索的代表性算法——爬山算法为例,基于Matlab解决给定算例的旅行商问题(TSP),以帮助大家更好地理解和学习Local Search方法。 

 二、爬山算法介绍 

 2.1  What is Hill Climbing Algorithm? 

爬山算法(Hill Climbing Algorithm)是一种局部择优的方法,也是一种简单的贪心搜索算法,该算法每次将当前解与邻居解进行对比,取其中的较优者作为新的当前解,直到当前解就是局部最优解。 

2.2  爬山算法的邻居解(neighbor)选择策略 

2.2.1  First improvement 

依次寻找当前解x的邻居解中首次出现的、比x更优的解,将这个解作为新的当前解。重复这一动作,直到当前解的邻居解中不再有更好的解,此时我们称该解为局部最优解(Local Optima)。 

2.2.2 Best improvement 

遍历当前解x的所有邻居解,选出邻居解中最优的解作为新的当前解。然后重复这一动作,直到当前解的邻居解中不再有更好的解。 

2. 3 伪代码 

以采用First improvement策略的爬山算法为例,伪代码如下[2] 

broken image

注:i 代表一个可行解(feasible solution),f代表要最小化的目标函数(objective function to be minimized),Si表示i的邻域(the neighborhood ofsolution i)。

伪代码的具体阐述如下: 

步骤1:选取一个初始解i 

步骤2:从当前解i的邻域Si中选取一个新解j 

步骤3:如果j的目标函数值小于i的目标函数值,那么j成为当前解 

步骤4:如果对于所有的j∈Si,都有f(j)≥f(i)( 即Si中所有解的目标值函数都大于等于i的目标函数值,i的目标函数值已经在其邻域中达到了最小),那么爬山结束,当前解就是局部最优解

步骤5:返回步骤2

三、用爬山算法解决TSP问题 

3.1  解决思路 

对于TSP问题,使用爬山算法可以快速地获得一条近似最优路径,以选用Best improvement策略为例,思路如下: 

(1)随机生成一个初始解i,把i作为当前解,计算该解的路径长度

(2)通过swap/insertion/2-opt算子生成当前解的邻域Si,计算每个邻居解的路径长度,并与当前解的路径长度比较

(3)如果邻域Si中存在比当前解更优的解,那么从中选取优化程度最大的解j,当前解更新为j 

(4)如果邻域Si中不存在比当前解更优的解,那么爬山算法结束,当前解即为局部最优解

(5)返回第二步

3.2 TSP问题的解决 

(1)解的表示 

在TSP问题中,对若干城市的不同访问顺序即为不同的解。假设有n个城市需要旅行商访问,那么可以对这些城市从1到n进行编号,不同的排列顺序即为不同的解。本文选用的就是该方法。 

例如,有8个城市需要访问,将这些城市从1到8进行编号,对编号数任意排列,即可生成一个解。比如(3,4,6,7,1,8,2,5)就是一个解。 

当然,TSP问题的解还有其它的表示方式:假设有n个城市需要旅行商访问,按照顺序记录每个城市访问的次序。 

例如有8个城市需要访问,依次记录每个城市的访问次序,即可生成一个解,比如(3,4,6,7,1,8,2,5),表示城市1是第三个进行访问的、城市2是第四个进行访问的、城市3是第六个进行访问的......城市8是第五个进行访问的

(2)初始解生成方式:随机生成 

(3)邻域算子:swap/insertion/2-opt 

  • Swap operator (AKA 2-exchange operator) ,生成方式如下图所示: 
broken image

 

如上图所示,交换两个元素的位置,即可生成一个邻居解。 

  • Insertion operator,生成方式如下图所示: 
broken image

 

如上图所示,选取一个节点,插入到不同的位置,即可生成一个邻居解。 

  • 2-edge exchange operator(AKA 2-opt),生成方式如下图所示: 
broken image

 

(4)目标函数 

TSP问题的目标函数如下: 

broken image

 

其中: s 表示一个解,s=(s1,...si...sn);

C(s)代表路径的长度; 

d(si,si+1)表示si到si+1的距离 

(5)邻居解(neighbor)选择策略:Best improvement/First improvement

(6)算法停止标准:若s的邻域为N(s),对于所有的s'∈N(s),都有C(s)≤C(s')

3.3 Matlab代码实现(代码入口见文末) 

本文使用TSP问题中经典的Berlin 8、Berlin 52算例,借助Matlab做了几组对比试验,其中rng代表随机种子,确保了实验结果的可复现性,实验结果如下: 

broken image

 由实验结果分析,邻域算子通过影响邻域大小来影响搜索次数,算法运行的时间与邻域搜索的次数有关,搜索的次数越多越耗时,因此如何减少不必要的搜索是优化的方向之一;目前没有足够研究结果表明first和best improvement哪一个更好;相同目标函数值对应的最优解不一定唯一;结合tsp问题的特征,2-opt邻域算子相比于swap和insert更适合该问题的求解的,2-opt的求解速度比较快,而且解的质量也能得到一定的保证。 

四、FAQ 

Q1:局部搜索的缺点有哪些? 

A1:容易陷入局部最优,且解的质量与初始解及邻域结构密切相关。 

Q2:针对上述缺点可采取的改进措施有哪些? 

A2:目前,主要利用三种手段打破局部搜索的局限,它们分别是: 

(1)从不同的初始解开始迭代 

第一种方法是运用多起点局部搜索(Multistart local search),随机生成若干初始解,从每个初始解出发进行搜索,找到各自对应的最优解,再从这些最优解中选择一个最好的作为最终的结果。另一种方法是迭代局部搜索(Iterative local search),它在局部搜索得到的局部最优解的基础上加入了扰动,然后重新进行局部搜索,其本质可概括为:迭代地构建一系列由嵌入型启发式生成的解,从而得到比使用该启发式的随机重复试验更好的解[2]。 

(2)改变搜索的“地形”(landscape) 

如采用变邻域搜索(Varible neighborhood search)对多个不同的邻域进行系统搜索。首先进行最小的邻域搜索,当无法改进解时,则切换到稍大一点的邻域。如果能继续改进解,则退回到最小的邻域,否则继续切换到更大的邻域。每变换一次邻域,相对于切换了一次搜索的“地形”(landscape)。此外,通过导引式局部搜索(Guided local search)、添加噪声(Noisy method)、平滑处理(Smoothing method)等途径改变问题的适应度函数(FitnessFunction),同样能达成更改搜索地形的目标。 

(3)接受一个较差的解 

代表方法有模拟退火(Simulated annealing)和禁忌搜索(Tuba search)。模拟退火以一定的概率接受差解,而禁忌搜索当解被禁时,就不得不接受差解中最好的解。 

优化策略如下图所示: 

broken image

Q3:Matlab打开.M文件时,中文注释变为乱码该如何解决? 

A3:中文注释乱码的原因很可能是由于不同版本之间的matlab的字符编码不统一,最简单的解决方法是用计算机自带的记事本打开该M文件,选择“另存为”,修改编码方式为“ANSI”,保存文件后重新打开即可。 

broken image
broken image
broken image

Q4:使用“2-opt”算子生成邻域时,“jj>=min(n+ii-2,n)”的限制条件有何作用? 

broken image

A4:能避免产生同质化的解。当删除该限制条件后,基于berlin8算例,选用2-opt算子及bestImprove策略运行Matlab主程序,以第4次爬山为例,输出结果如下图所示: 

broken image

可见,第6次operator产生的邻居解(3 7 2 1 5 6 4 8)与初始解(3 8 4 6 5 1 2 7)实际上是同质解,二者虽以不同顺序遍历了8个城市,但各城市两两间连线形成的“边”却是相同的,因此走过的距离必然相等。当邻域很大时,该条件的存在能省去大量重复的、无意义的计算,提高搜索效率。 

五、小结 

本文首先对TSP问题和LS算法做了简略介绍,并引入爬山算法;随后在Matlab中运用爬山算法求解TSP问题(附有代码),将理论与实践结合;最后指出了LS算法的不足和优化方法,列举并回答了初学者在探索过程中可能产生的疑问。 

囿于作者的学术水平,本文的诸多表述或许不够严谨和科学,用途仅限于优化算法——特别是局部搜索算法入门者的非正式学术交流,不足之处还望各位读者加以批评指正。 

参考文献 :

[1] 邢文训, 谢金星. 现代优化计算方法.第2版[M]. 清华大学出版社, 2005. 

相关资料:

作者:宋博文  张雨沁

校对:朱燕燕