首页 > 代码库 > 【算法杂谈】模拟退火算法

【算法杂谈】模拟退火算法

最近看到量子退火计算机【D-Ware】研制成功,博主表示十分的兴奋。

我就浅谈一下什么是【模拟退火算法】

----------------------【从爬山算法开始】----------------------------------

技术分享

【抱歉,图画的太渣】

好的,我们现在开始假设你在爬这座山,你的任务是在爬完这座山之后告诉我最高的点是哪里。

那么我们用爬山算法的思想来模拟一下。

首先,你来到了A,你心想:这里是我到过最高的地方了(......),所以在当前情况下最优值是A

一会,你来到了B,你发现B好像比A高了一点,于是在当前情况下最高的是B

然后,你发现你的脑容量不够了,为了更加快速,你只能记下左右两个点的高度,所以比较A与B,你忘记了A的高度。

悲催的事情发生了,你走到了C,发现这里真高,应该是世界上最高的地方了,你想左右看了看,发现B与D都没有C高,于是你认为C是这座山最高的点(……)

显而易见,你被局部最优解蒙蔽了双眼……

虽然这种方法对于处理超大规模数据速度很快,但你发现这种算法有着极大的BUG。

------------------------------------【模拟退火算法(SA)】----------------------------------------------------------

那我们如何跳过这个坑呢???
然后我们发现了一种(玄学)算法,既然你的脑容量只足以记住最优两个点的高度,那么我们何不试一试随便左右走走呢?

技术分享

【再回到这张图】

我们在用退火算法的思想来试一试

首先,你来到了A,你记住了这里的高度。

接着你跳过了B,直接(瞬移???)到了C,你发现这里好像很高,于是你记住了C。

然后你又随机(以一定的概率接受移动,而且这个概率随着时间推移逐渐降低(逐渐降低才能趋向稳定))跳到了D,你发现D真的很矮,但你向右看,你发现了E,于是你忘掉了C和A。

当你来到了E,你接着向F走,你发现F实在太矮了,再向右走你发现没有路了……

经历了 模拟,我们发下模拟退火算法成功的避开了陷入局部最优值的BUG。

这里的“一定的概率”的计算参考了金属冶炼的退火过程,这也是模拟退火算法名称的由来。

那么这个概率如何计算呢:

根据热力学的原理,在温度为T时,出现能量差为dE的降温的概率为P(dE),表示为:

    P(dE) = exp( dE/(kT) )

  其中k是一个常数,exp表示自然指数,且dE<0。这条公式说白了就是:温度越高,出现一次能量差为dE的降温的概率就越大;温度越低,则出现降温的概率就越小。又由于dE总是小于0(否则就不叫退火了),因此dE/kT < 0 ,所以P(dE)的函数取值范围是(0,1) 。

  随着温度T的降低,P(dE)会逐渐降低。

  我们将一次向较差解的移动看做一次温度跳变过程,我们以概率P(dE)来接受这样的移动。

 

让我们打一个形象的比喻:

爬山算法是一个鼠目寸光的人在爬山,他爬到了阿尔卑斯山觉得这是世界上最高的山,但不知道珠穆朗玛峰还在等着他。

模拟退火算法是一个喝醉酒了的人在爬山,他一开始随意的走着,他甚至有可能踏入盆地。但他逐渐清醒,并向高处奔去。

-------------------------------------【SA算法解决实际问题】-------------------------------------------------

SA(模拟退火)算法解决TSP(旅行商问题):有N个城市,唯一遍历所有城市,再回到出发的城市,求最短的路线。

当然SA算法是一种基于随机的算法,它能以非常快的速度获得近似解,有多快呢?O(n)。这个速度已经十分快速了,比起基于枚举的算法要快得多。

那么如何实际解决TSP问题呢?

旅行商问题属于所谓的NP完全问题(不知道的自己百度),精确的解决TSP只能通过穷举所有的路径组合,其时间复杂度是O(N!) 。So,要想尽快的解决TSP请使用SA算法。

  使用模拟退火算法可以比较快的求出TSP的一条近似最优路径。(使用遗传算法也是可以的,我将在下一篇文章中介绍)模拟退火解决TSP的思路:

1. 产生一条新的遍历路径P(i+1),计算路径P(i+1)的长度L( P(i+1) )

2. 若L(P(i+1)) < L(P(i)),则接受P(i+1)为新的路径,否则以模拟退火的那个概率接受P(i+1) ,然后降温

3. 重复步骤1,2直到满足退出条件

  产生新的遍历路径的方法有很多,下面列举其中3种:

1. 随机选择2个节点,交换路径中的这2个节点的顺序。

2. 随机选择2个节点,将路径中这2个节点间的节点顺序逆转。

3. 随机选择3个节点m,n,k,然后将节点m与n间的节点移位到节点k后面。

代码奉上!!!

#include <iostream> #include <cstdio>#include <cstdlib>#include <cmath>#include <ctime>//滑稽!!!!!! const int MAXN = 27; //城市数量const double MAX = 27.0; //城市数量const double INIT_T = 3000; //初始温度const double RATE = 0.95; //温度衰减率const double FINNAL_T = 1E-10; //终止温度const int IN_LOOP = 15000; //内循环次数const int LIMIT = 10000; //概率选择上限const int FINL_LOOP = 1000; //外层循环double DD=0;struct path{//定义路线结构	int citys[MAXN];	double length;}D_BestPath;struct point{//定义点结构	double x;	double y;}D_Point[MAXN];//计算点和点之间的距离void point_dist(){	int i, j;	double x;	for(i=0; i<MAXN; i++)	{		for(j=i+1; j<MAXN; j++)		{			x = (D_Point[i].x-D_Point[j].x)*(D_Point[i].x-D_Point[j].x);			x += (D_Point[i].y-D_Point[j].y)*(D_Point[i].y-D_Point[j].y);			D_Length[i][j] = sqrt(x);			D_Length[j][i] = D_Length[i][j];		}		}}//初始化void init(){	int i;	printf("初始状态路径:");	D_BestPath.length = 0;	for(i=0; i<MAXN; i++)	{//初始顺序经过路径		D_BestPath.citys[i] = i;		printf("%d--", i);	}	for(i=0; i<MAXN-1; i++)	{//计算路径长度		D_BestPath.length += D_Length[i][i+1];	}	printf("\n路径长度为:%.3lf\n\n", D_BestPath.length);			}void initi(){ //测试	int i;	D_BestPath.length = 0;	D_BestPath.citys[0] = 0;	D_BestPath.citys[1] = 1;	D_BestPath.citys[2] = 5;	D_BestPath.citys[3] = 4;	D_BestPath.citys[4] = 11;	D_BestPath.citys[5] = 12;	D_BestPath.citys[6] = 3;	D_BestPath.citys[7] = 17;	D_BestPath.citys[8] = 18;	D_BestPath.citys[9] = 19;	D_BestPath.citys[10] = 20;	D_BestPath.citys[11] = 9;	D_BestPath.citys[12] = 13;	D_BestPath.citys[13] = 14;	D_BestPath.citys[14] = 7;	D_BestPath.citys[15] = 6;	D_BestPath.citys[16] = 10;	D_BestPath.citys[17] = 8;	D_BestPath.citys[18] = 2;	D_BestPath.citys[19] = 16;	D_BestPath.citys[20] = 22;	D_BestPath.citys[21] = 21;	D_BestPath.citys[22] = 15;	D_BestPath.citys[23] = 26;	D_BestPath.citys[24] = 25;	D_BestPath.citys[25] = 24;	D_BestPath.citys[26] = 23;	for(i=0; i<MAXN-1; i++)	{//计算路径长度		D_BestPath.length += D_Length[D_BestPath.citys[i]][D_BestPath.citys[i+1]];	}}//输入城市坐标信息void input(){	int i;	for(i=0; i<MAXN; i++)		scanf("%lf%lf", &D_Point[i].x, &D_Point[i].y);}path getnext(path p){	path ret;	int i,  x, y;	int te;	ret = p;	do	{		x = (int)(MAX*rand()/(RAND_MAX + 1.0));		y = (int)(MAX*rand()/(RAND_MAX + 1.0));	}	while(x == y);	te = ret.citys[x];	ret.citys[x] = ret.citys[y];	ret.citys[y] = te;	ret.length = 0;	for(i=0; i<MAXN-1; i++)	{//计算路径长度		ret.length += D_Length[ret.citys[i]][ret.citys[i+1]];	}	DD++;	return ret;}void sa(){	int i, P_L=0, P_F=0;;	path curPath, newPath;	double T = INIT_T;	double p, delta;	srand((int)time(0));	curPath = D_BestPath;	while(true)	{		for(i=0; i<IN_LOOP; i++)		{			newPath = getnext(curPath);			delta = newPath.length - curPath.length;			if(delta < 0)			{//更新长度				curPath = newPath;				P_L = 0;				P_F = 0;			}			else			{				p = (double)(1.0*rand()/(RAND_MAX+1.0));				if(exp(delta/T) < 1 && exp(delta/T) > p)				{					curPath = newPath;				}				P_L ++;			}			if(P_L > LIMIT)			{				P_F ++;				break;			}		}		if(curPath.length < newPath.length) D_BestPath = curPath;		if(P_F > FINL_LOOP || T<FINNAL_T) break;	    T = T * RATE;	}}int main(){	input();	point_dist();	init();	sa();}

 打字真**累……

【算法杂谈】模拟退火算法