最近看了这个算法,然后实现了一下
详细内容参考:https://www.zhihu.com/people/xiao-y-99-23/activities
模拟退火算法的核心思想是:首先随机选择一个解作为开始,接下来产生一个随机扰动,如果找到比上一个解更接近最优解的解,那么就直接接受这个解。而如果找到的解离得更远了,没关系,以一定的概率接受。为什么要以一定概率接受?因为要得到全局最优解,避免算法在处理有多个极值点的函数时得到的是局部最优解。
假设方程:y = x + 10 * sin(5 * x) + 7 * cos(4 * x);
求解[0,9]范围内函数的最大值:y,x
保存下面代码为文件xxx.c,然后执行命令编译,运行成功后即可看到程序输出
[root@localhost test]# gcc -g -lm xxx.c -o xxx
[root@localhost test]# ./xxx
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
/* 方程 */
float val(float x) {
return x + 10 * sin(5 * x) + 7 * cos(4 * x);
}
/* 生成随机数 */
float nrand() {
return rand() / (double)RAND_MAX;
}
/* 判断函数,判断是否接受新解 */
int judge(float dE, float t)
{
int y;
if (dE < 0) {
y = 1;
} else {
float d = exp(-(dE / t));
if (d > nrand()) {
y = 1;
} else {
y = 0;
}
}
return y;
}
/* 取值范围 */
int section_l = 0;
int section_h = 9;
/* 初始温度、停止温度与降温系数 */
float tmp = 1e5;
float tmp_min = 1e-3;
float alpha = 0.98;
/* 计数器 */
int counter = 0;
int main()
{
srand((unsigned)time(NULL));
/* 生成初始随机解 */
float x_old = (section_h - section_l) * nrand() + section_l;
float x_new = x_old;
float s_old = val(x_old);
float s_new = s_old;
/* 退火 */
while (tmp > tmp_min) {
/* 随机扰动 */
float delta = (nrand() - 0.5) * 3;
x_new = x_old + delta;
/* 扰动的值小于一半的区间范围时,可以用这种方法防止新值超出区间范围 */
if (x_new < section_l || x_new > section_h) {
x_new = x_new - 2 * delta;
}
s_new = val(x_new);
/* 求差值,这里是找最大值而非最小值,所以不是s_new - s_old */
float dE = s_old - s_new;
/* 判断 */
float j = judge(dE, tmp);
if (j) {
s_old = s_new;
x_old = x_new;
printf("c=%-4d, y=%.4f, x=%.4f\n", counter, s_old, x_old);
}
/* 只有当dE < 0的情况下才降温 */
if (dE < 0) {
tmp = tmp * alpha;
} else {
counter = counter + 1;
}
/* 当接受更差的解的概率太小时,若又长时间找不到更优的解,那么退出循环,结束算法 */
if (counter > 10000) {
break;
}
}
printf("y=%.4f,x=%.4f\n", s_old, x_old);
return 0;
}
/*
c=9775, y=24.7403, x=7.8315
c=9812, y=24.8076, x=7.8405
c=9881, y=24.8380, x=7.8469
c=9949, y=24.8552, x=7.8578
y=24.8552,x=7.8578
*/
#include <math.h>
#include <stdlib.h>
#include <time.h>
/* 方程 */
float val(float x) {
return x + 10 * sin(5 * x) + 7 * cos(4 * x);
}
/* 生成随机数 */
float nrand() {
return rand() / (double)RAND_MAX;
}
/* 判断函数,判断是否接受新解 */
int judge(float dE, float t)
{
int y;
if (dE < 0) {
y = 1;
} else {
float d = exp(-(dE / t));
if (d > nrand()) {
y = 1;
} else {
y = 0;
}
}
return y;
}
/* 取值范围 */
int section_l = 0;
int section_h = 9;
/* 初始温度、停止温度与降温系数 */
float tmp = 1e5;
float tmp_min = 1e-3;
float alpha = 0.98;
/* 计数器 */
int counter = 0;
int main()
{
srand((unsigned)time(NULL));
/* 生成初始随机解 */
float x_old = (section_h - section_l) * nrand() + section_l;
float x_new = x_old;
float s_old = val(x_old);
float s_new = s_old;
/* 退火 */
while (tmp > tmp_min) {
/* 随机扰动 */
float delta = (nrand() - 0.5) * 3;
x_new = x_old + delta;
/* 扰动的值小于一半的区间范围时,可以用这种方法防止新值超出区间范围 */
if (x_new < section_l || x_new > section_h) {
x_new = x_new - 2 * delta;
}
s_new = val(x_new);
/* 求差值,这里是找最大值而非最小值,所以不是s_new - s_old */
float dE = s_old - s_new;
/* 判断 */
float j = judge(dE, tmp);
if (j) {
s_old = s_new;
x_old = x_new;
printf("c=%-4d, y=%.4f, x=%.4f\n", counter, s_old, x_old);
}
/* 只有当dE < 0的情况下才降温 */
if (dE < 0) {
tmp = tmp * alpha;
} else {
counter = counter + 1;
}
/* 当接受更差的解的概率太小时,若又长时间找不到更优的解,那么退出循环,结束算法 */
if (counter > 10000) {
break;
}
}
printf("y=%.4f,x=%.4f\n", s_old, x_old);
return 0;
}
/*
c=9775, y=24.7403, x=7.8315
c=9812, y=24.8076, x=7.8405
c=9881, y=24.8380, x=7.8469
c=9949, y=24.8552, x=7.8578
y=24.8552,x=7.8578
*/