1简介:
模拟退火来自冶金学的专有名词退火。退火是将材料加热后再经特定速率冷却,目的是增大晶粒的体积,并且减少晶格中的缺陷。材料中的原子原来会停留在使内能有局部最小值的位置,加热使能量变大,原子会离开原来位置,而随机在其他位置中移动。退火冷却时速度较慢,使得原子有较多可能可以找到内能比原先更低的位置。
模拟退火的原理也和金属退火的原理近似:我们将热力学的理论套用到统计学上,将搜寻空间内每一点想象成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。算法会计算原有的解的适合度与新的解的适合度的差值,通过Metropolis准则计算出概率来决定是否接受新的解。在Metropolis准则之下,在开始的阶段更容易接受较差的解,而随着时间的推移,较差的解越来越难以被接受,并最终稳定(即收敛)。这种接受较差的解的特性,使得模拟退火算法有能力跳出局部最优解,找到全局最优解。
模拟退火是一种通用概率算法,常用来在一定时间内寻找在一个很大搜寻空间中的近似最优解。
2算法要点:
从上述简介中不难总结出算法设计的几个要点,首先要设计检测命题适合程度的函数,从而计算适合度的大小进行比较;另外在进行计算概率的时候根据的是Metropolis准则,即在温度T时趋于平衡的概率为,其中E为温度T时的内能,ΔE为其改变量,k为Boltzmann常数。其中的ΔE在算法中化为两个解适合度的差值,T为控制参数。在T足够大的时候,适合度差值较大的情况也能有较大的几率接受新较差的解,而随着T不断变小,适合度差值也只有在较小的范围内才能接受新的较差的解,并最终稳定,输出最优解。
3演算步骤:
初始化:
定义初始温度参数T和降温系数q,随机生成一组当前问题的解,根据适合度函数计算适度值f1。
迭代过程:
迭代过程是模拟退火算法的核心步骤,分为新解的产生和接受新解两部分:
1)由一个产生函数从当前解产生一个位于解空间的新解;为便于后续的计算和接受,减少算法耗时,通常选择由当前新解经过简单地变换即可产生新解的方法,如对构成新解的全部或部分元素进行置换、互换等,注意到产生新解的变换方法决定了当前新解的邻域结构,因而对冷却进度表的选取有一定的影响。
2)计算与新解所对应的目标函数差。因为目标函数差仅由变换部分产生,所以目标函数差的计算最好按增量计算。事实表明,对大多数应用而言,这是计算目标函数差的最快方法。
3)判断新解是否被接受,判断的依据是一个接受准则,最常用的接受准则是Metropolis准则:若Δt′<0则接受S′作为新的当前解S,否则以概率exp(-Δt′/T)接受S′作为新的当前解S。
4)当新解被确定接受时,用新解代替当前解,这只需将当前解中对应于产生新解时的变换部分予以实现,同时修正目标函数值即可。此时,当前解实现了一次迭代。可在此基础上开始下一轮试验。而当新解被判定为舍弃时,则在原当前解的基础上继续下一轮试验。
模拟退火算法与初始值无关,算法求得的解与初始解状态S(是算法迭代的起点)无关;模拟退火算法具有渐近收敛性,已在理论上被证明是一种以概率1收敛于全局最优解的全局优化算法;模拟退火算法具有并行性。
停止准则:
温度T降至某最低值时,完成给定数量迭代中无法接受新解,停止迭代,接受当前寻找的最优解为最终解。
退火方案:
在某个温度状态T下,当一定数量的迭代操作完成后,降低温度T,在新的温度状态下执行下一个批次的迭代操作。
代码如下:
#include
#include
#include
#include
#include
#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