退火算法解决tsp问题

首先强烈推荐一篇博文http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html

个人感觉退火算法明显比遗传算法理解简单,实现也更加方便.

首先上公式:

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

p(de)是指在当前的策略并不是那么优秀的情况下接受它的概率,所以de一定为负数,k是一个常数(本程序中取1),T为当前的温度

这就涉及一个问题,当前的策略并不优秀,为什么要接受它,这就提到贪心之类的算法只盯着每一步的最优解,很容易陷入局部最优。

而有时我们要下这座山才能爬上另外一座更高的山.

而T就是退火算法的由来,随着时间降低T逐渐降低,所以1/T变大,而之前说过de一定为负数,所以exp(de/kT)变小,也就是说随着时间的推移我们越来越只接受较好的策略。

一个很简单的比喻

兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火。

那么当前策略优于之前的策略呢,那还等什么,赶快拿过来用啊...(直接更新,也就是说退火算法就是当要舍弃新产生的策略并不是直接丢弃)

while( T > T_min )
{
  dE = J( Y(i+1) ) - J( Y(i) ) ;

  if ( dE >=0 ) //表达移动后得到更优解,则总是接受移动
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
  else
  {
// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也
if ( exp( dE/T ) > random( 0 , 1 ) )
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动
  }
  T = r * T ; //降温退火 ,0<r<1 。r越大,降温越慢;r越小,降温越快
  /*
  * 若r过大,则搜索到全局最优解的可能会较高,但搜索的过程也就较长。若r过小,则搜索的过程会很快,但最终可能会达到一个局部最优值
  */
  i ++ ;
}

   对于TSP问题J函数就是路程,但是注意路程越大,越应该舍弃.

  模拟退火解决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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#define cities 10//城市的个数
#define T  4000//定义初始温度
#define delta 0.98//用来控制降温的快慢
#define eps 0//结束时的温度
int distance[cities+1][cities+1];
int city[cities]; //访问的顺序
void init()
{
    int i,j;
    memset(distance,0,sizeof(distance));
    srand(time(NULL));
    for (i=0;i<cities;i++)
        for (j=i+1;j<cities;j++)
    {
       distance[i][j]=rand()%100;
       distance[j][i]=distance[i][j];
    }
    printf("城市距离矩阵如下:\n");
    for (i=0;i<cities;i++)
    {
    for (j=0;j<cities;j++)
    printf("%4d",distance[i][j]);
    printf("\n");
    }
}
void produce()//生成初始路径
{
    int i,j,t,ok;
    srand(time(NULL));
    for (i=0;i<cities;i++)
    {
        t=rand()%cities;
        ok=1;
        for (j=0;j<i;j++)
         if (city[j]==t)
            {
                ok=0;
                break;
            }
         if (ok)
         city[i]=t;
         else
         i--;
    }
    printf("初始选择为:\n");
    for (i=0;i<cities;i++)
        printf("%4d",city[i]);
        printf("\n");
}
int main()
{
    int i,j,t,temp1,temp2,before,temp,last,min;
    double d;
    init();//生成距离矩阵
    produce();//生成初始路径
    t=T;
    srand(time(NULL));//生成新路径的方案是随机生成两个节点,交换这两个节点内片段的位置
    while(t>eps)//生成一条新路径,如果新路径优于当前路径,那么采纳,否则以一定几率采纳(随时间降低)
    {
        before=0;
        for (i=1;i<cities;i++)
        before=before+distance[city[i-1]][city[i]];//计算原来的距离
        temp1=rand()%cities;
        temp2=rand()%cities;
        if (temp1>temp2)//保证temp1<temp2
        {
            temp=temp1;
            temp1=temp2;
            temp2=temp1;
        }
        for (i=temp1;i<=(temp1+temp2)/2;i++)
        {
            temp=city[i];
            city[i]=city[temp2+temp1-i];
            city[temp2+temp1-i]=temp;
        }
        last=0;
        for (i=1;i<cities;i++)
        last=last+distance[city[i-1]][city[i]];//计算现在的距离
        printf("%d %d\n",before,last);
       if (last>before)//如果新的不好并且不可能继续
        {
            d=rand()%100;
            d=d/100;
            if (exp((before-last)*1.0/t)<d) //注意这边是before-last(一开始效果很不好,竟然是这边...),因为是要保证负的,这样exp肯定小于1,否则每一次都接受较大的那个了...
            {
               for (i=temp1;i<=(temp1+temp2)/2;i++)//再换回来
             {
            temp=city[i];
            city[i]=city[temp2+temp1-i];
            city[temp2+temp1-i]=temp;
             }
            }
         }
         t=t*delta;
     }
        printf("最优解为:\n");
        for (i=0;i<cities;i++)
        printf("%4d",city[i]);
        printf("\n");
        printf("最短路长度为:\n");
        before=0;
        for (i=1;i<cities;i++)
        before=before+distance[city[i-1]][city[i]];//计算原来的距离
        printf("%d\n",before);
        return 0;
}

效果相当nice(比遗传算法效果还好,可能是遗传算法的交配部分设计的还是不够优秀),而且最后基本都收敛了.




  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值