模拟退火算法是一种通用的优化算法,虽然不一定能找到最优解,但能在较短时间内找到较优解。下面让我们一起学习一下吧。
1、算法介绍
问题提出:首先介绍一个“盲人爬山问题”:有一个盲人想爬到一堆山里最高的山峰,请问在没有任何帮助的情况下他在怎么完成自己的目标?
爬山算法:聪明的小伙伴可能会想到,盲人在爬山时可以通过自动的手判断自己是在上山还是下山,每次都往上山的方向走。当无论他往哪个方向走,都是下山时,就可以判断他已经到达了某一个山峰了。但是这种方法只能保证他能到一个小高峰,没办法保证他到达全局最高峰。这种方法被成为爬山算法。
爬山算法是一种完完全全的贪心算法,这种方法非常简单,但是却只能让盲人到达某一个小高峰,没办法达到全局最高峰。那么有没有办法可以解除这种贪心诅咒呢?
模拟退火算法就可以看作爬山算法的改进版本,他在搜索过程中引入了随机因素,以特定概率p接受较差解,然后就有可能会跳出局部最优解的陷阱。举个栗子,这个盲人在上山前喝了1斤地瓜烧,上山的时候头晕脑胀,他在每一确定爬的方向的时候,都因为喝多了,存在可能选错了方向。随着他慢慢爬山,他越来越清醒,选错方向的可能也越来越小。这样就有可能破除小山峰的魔咒,爬到最高的山峰上。
那么这个算法跟退火又有什么关系呢?关键就在这个特定概率p上面。根据热力学第二定律,我们可以推导出来在温度为T时,出现能量差为de的降温的概率为e^(de/T)。这个概率随着T的降低而降低,因此可以用来描述概率。
2、算法流程图
3、代码实现
我们来拿模拟退火算法来解决一下TSP问题,看看这个算法是效果怎么样,这里简单介绍一下TSP问题:
旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。TSP问题是一个[NPC](https://baike.baidu.com/item/NPC/6584问题。
——来自百度百科
下面是通过模拟退火算法来计算中国31个城市的TSP问题,坐标数据来自网络
#先导入math库已进行数学运算,导入random库以生成随机数importmathimportrandom#data用来存储中国31个城市的坐标data=[(1304,2312),(3639,1315),(4177,2244),(3712,1399),(3488,1535),(3326,1556),(3238,1229),(4196,1004),(4312,790),(4386,570),(3007,1970),(2562,1756),(2788,1491),(2381,1676),(1332,695),(3715,1678),(3918,2179),(4061,2370),(3780,2212),(3676,2578),(4029,2838),(4263,2931),(3429,1908),(3507,2367),(3394,2643),(3439,3201),(2935,3240),(3140,3550),(2545,2357),(2778,2826),(2370,2975)]#cj是我们的目标函数,这里的函数不包括从最后一个点到第一个点之间的距离defcj(ki):sum=0foriinrange(0,30):sum=sum+math.sqrt(pow((data[ki[i]][0]-data[ki[i+1]][0]),2)+pow((data[ki[i]][1]-data[ki[i+1]][1]),2))#sum=sum+math.sqrt(pow((data[ki[0]][0]-data[ki[30]][0]),2)+pow((data[ki[0]][1]-data[ki[30]][1]),2))returnsum#xold是我们开始随机生成的第一个可行解xold=[]foriinrange(0,31):xold.append(i)xnew=xold[:]#tmax是我们设定的初始最高温度,tmin是我们设定的最低温度,cx为我们的降温系数tmax=30000tmin=1e-8cx=0.98#count用来计算新解连续没有被采用的次数count=0#get_new_jie用来生成随机数交换两个城市之间的位置以生成新解(手动滑稽)defget_new_jie(xold):a=random.randint(0,30)b=random.randint(0,30)tempold=xold[:]temp=tempold[a]tempold[a]=tempold[b]tempold[b]=temptempnew=tempoldreturntempnew#主题退火过程while(tmax>tmin):#在tmax温度下找到最优的解xnewforiinrange(1,5000):#如果xnew更优,则直接替换xold,否者以概率d=pow(math.e,-de/tmax)接受该解tempnew=get_new_jie(xold)de=cj(tempnew)-cj(xold)ifde<0:count=1xold=xnewxnew=tempnewelse:count=count+1d=pow(math.e,-de/tmax)ifrandom.random()<d:xold=xnewxnew=tempnewelse:pass#退火tmax=tmax*cx#如果连续5000次以上新解没有被接受,则说明已有解xold已经足够优,因此可以退出退火ifcount>5000:breakprint(count)print(xnew)print(cj(xnew))
4、效果分析
计算结果,嗯,还行,因为模拟退火算法只是在概率上收缩到最优解,所以多跑几遍可能会有更优的解。
5、总结分析
爬山算法过于贪心,在获得一个局部最优解之后,便安居一偶,不思进取。模拟退火算法则如同一条鲶鱼,扰动了安稳的过程,使获得最优解成为可能。所以说变则通,不变则亡。
这,是一个哲学问题。