模拟退火算法与C语言实现(TSP问题)

1简介:

模拟退火来自冶金学的专有名词退火。退火是将材料加热后再经特定速率冷却,目的是增大晶粒的体积,并且减少晶格中的缺陷。材料中的原子原来会停留在使内能有局部最小值的位置,加热使能量变大,原子会离开原来位置,而随机在其他位置中移动。退火冷却时速度较慢,使得原子有较多可能可以找到内能比原先更低的位置。

模拟退火的原理也和金属退火的原理近似:我们将热力学的理论套用到统计学上,将搜寻空间内每一点想象成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。算法会计算原有的解的适合度与新的解的适合度的差值,通过Metropolis准则计算出概率来决定是否接受新的解。在Metropolis准则之下,在开始的阶段更容易接受较差的解,而随着时间的推移,较差的解越来越难以被接受,并最终稳定(即收敛)。这种接受较差的解的特性,使得模拟退火算法有能力跳出局部最优解,找到全局最优解。

模拟退火是一种通用概率算法,常用来在一定时间内寻找在一个很大搜寻空间中的近似最优解。

2算法要点:

从上述简介中不难总结出算法设计的几个要点,首先要设计检测命题适合程度的函数,从而计算适合度的大小进行比较;另外在进行计算概率的时候根据的是Metropolis准则,即在温度T时趋于平衡的概率为,其中E为温度T时的内能,ΔE为其改变量,k为Boltzmann常数。其中的ΔE在算法中化为两个解适合度的差值,T为控制参数。在T足够大的时候,适合度差值较大的情况也能有较大的几率接受新较差的解,而随着T不断变小,适合度差值也只有在较小的范围内才能接受新的较差的解,并最终稳定,输出最优解。

3演算步骤:

初始化:

定义初始温度参数T和降温系数q,随机生成一组当前问题的解,根据适合度函数计算适度值f1。

迭代过程:

迭代过程是模拟退火算法的核心步骤,分为新解的产生和接受新解两部分:

扫描二维码关注公众号,回复: 3356214 查看本文章

1)由一个产生函数从当前解产生一个位于解空间的新解;为便于后续的计算和接受,减少算法耗时,通常选择由当前新解经过简单地变换即可产生新解的方法,如对构成新解的全部或部分元素进行置换、互换等,注意到产生新解的变换方法决定了当前新解的邻域结构,因而对冷却进度表的选取有一定的影响。

2)计算与新解所对应的目标函数差。因为目标函数差仅由变换部分产生,所以目标函数差的计算最好按增量计算。事实表明,对大多数应用而言,这是计算目标函数差的最快方法。

3)判断新解是否被接受,判断的依据是一个接受准则,最常用的接受准则是Metropolis准则:若Δt′<0则接受S′作为新的当前解S,否则以概率exp(-Δt′/T)接受S′作为新的当前解S。

4)当新解被确定接受时,用新解代替当前解,这只需将当前解中对应于产生新解时的变换部分予以实现,同时修正目标函数值即可。此时,当前解实现了一次迭代。可在此基础上开始下一轮试验。而当新解被判定为舍弃时,则在原当前解的基础上继续下一轮试验。

模拟退火算法与初始值无关,算法求得的解与初始解状态S(是算法迭代的起点)无关;模拟退火算法具有渐近收敛性,已在理论上被证明是一种以概率1收敛于全局最优解的全局优化算法;模拟退火算法具有并行性。

停止准则:

温度T降至某最低值时,完成给定数量迭代中无法接受新解,停止迭代,接受当前寻找的最优解为最终解。

退火方案:

在某个温度状态T下,当一定数量的迭代操作完成后,降低温度T,在新的温度状态下执行下一个批次的迭代操作。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>

#define T_start 5000.0      //初始温度
#define T_end (1e-8)        //结束温度
#define q 0.98              //退火系数
#define L 1000              //每个温度最大迭代次数
#define N 34                //城市个数
int city_result[N];         //城市列表的解空间
double city[N][2] = {{9932, 4439}, {10109, 4351}, {11552, 3472}, {10302, 3290}, {8776, 3333}, {7040, 4867}, {9252, 4278}, {9395, 4539}, {11101, 2540}, {9825, 5087}, {10047, 4879}, {10227, 4648}, {100027, 4229}, {9878, 4211}, {9087, 4065}, {10438, 4075}, {10382, 3865}, {11196, 3563}, {11075, 3543}, {11544, 3365}, {11915, 2900}, {11305, 3189}, {11073, 3137}, {10950, 3394}, {11576, 2575}, {12239, 2785}, {11529, 2226}, {9328, 4006}, {10012, 3811}, {9952, 3410}, {10612, 2954}, {10349, 2784}, {11747, 2469}, {11673, 2461}}; //中国34个城市实际距离坐标
//函数声明
//double city[N][2] = {{0,0},{3,0},{3,4}};//测试数据
double distance(double *city1, double *city2); //计算两城市间距离
double path(int city_result[N]);               //计算总路径
void init();                                   //初始化解空间
void creat();                                  //生成新的解空间

int main()
{
    time_t start, end;      //记录程序开始结束时间
    double time_sum;        //记录程序运行时间
    start = clock();        //程序开始时间
    int i, count;           //降温计数器
    int city_copyresult[N]; //拷贝解空间
    double path1, path2;    //原有解空间,新解空间的总路径
    double dE;              //原有解空间与新解空间的差值
    double r;               //随机产生0~1的值,是否接受新的解
    double T;               //当前温度
    srand(time(NULL));
    init(); //初始化解空间
    T = T_start;//初始温度赋值
    while (T > T_end) //当前温度大于结束温度
    {
        for (i = 0; i < L; i++)
        {
            memcpy(city_copyresult, city_result, N * sizeof(int));
            creat(); //产生新的解空间
            path1 = path(city_copyresult);
            path2 = path(city_result);
            dE = path2 - path1;
            if (dE > 0) //Metropolis准则
            {
                r = rand() / (RAND_MAX);
                if (exp(-dE / T) <= r)                                     //高温状态下,可以接受能量差值较大的新状态;低温状态下,则只能接受能量差值较小的新状态
                    memcpy(city_result, city_copyresult, N * sizeof(int)); //保留原来的解
            }
        }
        T *= q;
        count++;
    }
    end = clock();  //程序结束时间
    time_sum = (double)(end - start ) / (CLOCKS_PER_SEC);    //换算成秒
    printf("共降温:%d次\n", count);
    printf("经过模拟退火算法得出最优路径长度为:%f\n", path(city_result));
    for (i = 0; i < N; i++)
    {
        printf("%d->", city_result[i]);
    }
    printf("%d\n", city_result[0]);
    printf("程序共耗时%f秒.\n",time_sum);
    system("pause");
    return 0;
}

double distance(double *city1, double *city2) //计算两个城市之间的距离
{
    double x1, x2, y1, y2, dis;
    x1 = *city1;       //第一个城市的x坐标
    x2 = *city2;       //第二个城市的x坐标
    y1 = *(city1 + 1); //第一个城市的y坐标
    y2 = *(city2 + 1); //第二个城市的y坐标
    dis = sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2));
    return dis;
}

double path(int city_result[N]) //计算总路径
{
    int i, city1_num, city2_num; //解空间中的两个城市序号
    double sum = 0;              //路径总长度
    for (i = 0; i < N - 1; i++)  //解空间中首位到末位的总路径
    {
        city1_num = city_result[i];
        city2_num = city_result[i + 1];
        sum += distance(city[city1_num], city[city2_num]);
    }
    sum += distance(city[0], city[N - 1]); //加上解空间中末位到首位的路径
    return sum;
}

void init() //初始化解空间
{
    int i;
    for (i = 0; i < N; i++)
        city_result[i] = i; //顺序生成解空间
}

void creat() //生成新的解空间
{
    int point1, point2, temp;
    point1 = rand() % N;
    point2 = rand() % N;
    temp = city_result[point1];
    city_result[point1] = city_result[point2];
    city_result[point2] = temp;
}

本文参考:

https://zh.wikiphttps://zh.wikipedia.org/wiki/%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%ABedia.org/wiki/%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB

https://www.cnblogs.com/lyrichu/p/6688459.html

猜你喜欢

转载自blog.csdn.net/zhuzilong2013/article/details/82821348