模拟退火算法(搜索过程引入随机因素的贪心法,具有全局优化的性能)
1.目的
求问题的最优化解;
2.原理解释
模仿物理退火的过程。高温物体的粒子可从高能态转换为低能态,而从低能态转为高能态则有一定的随机性,物体温度越低越难从低能转换为高能,最终所有粒子转为最低能量态。
对应的物理准则:Metropolis准则
假设在状态 Xold 时,系统受到某种扰动使其状态变为 Xnew 。与此对应,系统的能量也从 E(Xold) 变为 E(Xnew) 。系统由状态 Xold 变为状态 Xnew 的接收概率P:
第二行的解释:温度越高,出现一次能量差为dE的降温的概率就越大,温度越低,出现降温的概率就越小;
3.与物理退火过程对应的算法的要素
(1)状态空间(搜索空间):有可行解的集合组成;
(2)状态产生函数(领域函数):内能E 转换为目标函数值f,应尽可能保证产生的候选解遍历全部解空间;
(3)状态转移概率:近似于Metroplis准则,接受一个新解为当前解的概率,与温度参数T有关;
(4)冷却进度表T(t):即降温管理表,用来控T下降的函数,假设时刻t的温度用T(t)来表示,t的含义也不一定只能是时间,比如:
第三个式子与t无关,k是进行迭代的次数。
温度T演化为控制参数;
(5)初始温度 T0 : 确定初温应折中考虑优化质量与效率;
(6)抽样稳定准则:应保证连续若干步迭代所得的值变化较小且按一定的步数抽样;
(7)算法终止准则:设置终止温度,设置迭代次数,搜索得到的最终值连续若干步保持不变。
4.与物理退火过程对应的算法理论:
在进行优化问题的领域搜索的时候引入退火规则。
下一状态比当前状态更优时接受新状态,反之有一定的概率接受新状态,该概率随着迭代的进行或温度的下降不断减小,最终趋近于零。
> 5.模拟退火算法的模型
模拟退火算法可以分解为解空间、目标函数和初始解三部分。
模拟退火的基本思想:
(1) 初始化:初始温度T(充分大),初始解状态S(是算法迭代的起点), 每个T值的迭代次数L
(2) 对k=1,……,L做第(3)至第6步:
(3) 产生新解S′
(4) 计算增量Δt′=C(S′)-C(S),其中C(S)为评价函数
(5) 若Δt′<0则接受S′作为新的当前解,否则以概率exp(-Δt′/T)接受S′作为新的当前解.
(6) 如果满足终止条件则输出当前解作为最优解,结束程序。
终止条件通常取为连续若干个新解都没有被接受时终止算法。
(7) T逐渐减少,且T->0,然后转第2步。
6.模拟退火算法的流程图:
还有一种清晰理解版:
模拟退火解决TSP问题(修改原博主bug后添加注释,转载并测试成功,,原博客为参考第三个):
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#define N 30 //城市数量
#define T 3000 //初始温度
#define EPS 1e-8 //终止温度
#define DELTA 0.98 //温度衰减率
#define LIMIT 1000 //概率选择上限
#define OLOOP 100 //外循环次数
#define ILOOP 1500 //内循环次数
using namespace std;
//定义路线结构体
struct Path
{
int citys[N];
double len;
};
//定义城市点坐标
struct Point
{
double x, y;
};
Path path; //记录最优路径
Point p[N]; //每个城市的坐标
double w[N][N]; //两两城市之间路径长度
int nCase; //测试次数
double dist(Point A, Point B)
{
return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
void GetDist(Point p[], int n)
{
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
w[i][j] = w[j][i] = dist(p[i], p[j]);
}
void Input(Point p[], int &n)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
}
//初始化
void Init(int n)
{
nCase = 0;
path.len = 0;
for(int i = 0; i < n; i++)
{
path.citys[i] = i;
if(i != n - 1)
{
printf("%d--->", i);
path.len += w[i][i + 1];
}
else
printf("%d\n", i);
}
printf("\nInit path length is : %.3lf\n", path.len);
}
//画出路线图
void Print(Path t, int n)
{
printf("Path is : ");
for(int i = 0; i < n; i++)
{
if(i != n - 1)
printf("%d-->", t.citys[i]);
else
printf("%d\n", t.citys[i]);
}
printf("\nThe path length is : %.3lf\n", t.len);
}
//随机抽取两座城市并交换两座城市记录在旅行链中的顺序
Path GetNext(Path p, int n)
{
Path ans = p;
int x = (int)(n * (rand() / (RAND_MAX + 1.0)));//随机取两座城市
int y = (int)(n * (rand() / (RAND_MAX + 1.0)));
while(x == y)
{
x = (int)(n * (rand() / (RAND_MAX + 1.0)));
y = (int)(n * (rand() / (RAND_MAX + 1.0)));
}
swap(ans.citys[x], ans.citys[y]);
ans.len = 0;
for(int i = 0; i < n - 1; i++)
ans.len += w[ans.citys[i]][ans.citys[i + 1]];//求得新路径的长度
cout << "nCase = " << nCase << endl;
Print(ans, n);//输出新解的路径
nCase++;
return ans;
}
void SA(int n)
{
double t = T;//初始温度
// srand(time(NULL));
Path curPath = path;
Path newPath = path;
int P_L = 0;
int P_F = 0;
while(1){ //外循环,主要更新参数t,模拟退火过程
for(int i = 0; i < ILOOP; i++){ //内循环,寻找在一定温度下的最优值
newPath = GetNext(curPath, n);
double dE = newPath.len - curPath.len;
if(dE < 0){ //如果找到更优值,直接更新
curPath = newPath;
P_L = 0;
P_F = 0;
}
else{
double rd = rand() / (RAND_MAX + 1.0);//即为[0,1)的随机数
if(exp(-dE / t) > rd && exp(-dE / t) < 1) //如果找到比当前更差的解,以一定概率接受该解,并且这个概率会越来越小
curPath = newPath;
P_L++;//不管有没有接受新解,P_L加一
}
if(P_L > LIMIT){//如果很多次没有接受新解,
P_F++;
break;//break只对循环起作用,无视if
}
}
if(curPath.len < newPath.len)//r如果很多次没有接受更好解
path = curPath;
if(P_F > OLOOP || t < EPS)
break;
t *= DELTA;
}
}
int main()
{
int n;
Input(p, n);// 输入城市坐标
GetDist(p, n);//计算每两座城市间的距离
Init(n);//初始化
SA(n);//模拟退火
Print(path, n);
printf("Total test times is : %d\n", nCase);
return 0;
}
模拟退火解决01背包问题:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define T0 1000
#define TF 0.01
#define T 0.95
#define N 1000
#define M 50
int weight[M]= { 80, 82, 85, 70, 72, 70, 66, 50, 55, 25,
50, 55, 40, 48, 50, 32, 22, 60, 30, 32,
40, 38, 35, 32, 25, 28, 30, 22, 50, 30,
45, 30, 60, 50, 20, 65, 20, 25, 30, 10,
20, 25, 15, 10, 10, 10, 4, 4, 2, 1 },
profit[M]= { 220, 208, 198, 192, 180, 180, 165, 162, 160, 158,
155, 130, 125, 122, 120, 118, 115, 110, 105, 101,
100, 100, 98, 96, 95, 90, 88, 82, 80, 77,
75, 73, 72, 70, 69, 66, 65, 63, 60, 58,
56, 50, 30, 20, 15, 10, 8, 5, 3, 1},
contain = 1000;
int premaxp = 0, bestmaxp = 0;
int a[M] = {0}, preseq[M] = {0}, bestseq[M]={0};
int calprofit(int a[M]) //计算总利润
{
int i = 0;
int p = 0;
for (i = 0; i < M; i++)
p = p + (a[i] * profit[i]);
return p;
}
int calweight(int a[M])
{
int i = 0;
int w = 0;
for (i = 0; i < M; i++)
{
w = w + (weight[i] * a[i]); //a[i]==0代表不放,==1代表放
}
return w;
}
void initialize(void)
{
int i = 0;
for (i = 0; i < M; i++)
{
a[i] = 0;
preseq[i] = 0;
bestseq[i] = 0;
}
premaxp = calprofit(a);
bestmaxp = premaxp;
}
void getrand(int *i, int *j)
{
*i = 0;
*j = 0;
while (*i == *j)
{
*i = rand()%50;
*j = rand()%50;
}
}
void change(void)
{
int r1 = 0, r2 = 0;
getrand(&r1, &r2); //随机取两个物品
a[r1] = 1;
a[r2] = 1;
if (calweight(a) > contain)
{
a[r2] = 0;
} //如果总重量超出,则不放入
if (calweight(a) > contain)
{
a[r1] = 0;
}
}
void SA(void)
{
double t = T0; //设定初始温度
int i = 0, j = 0;
int dif1 = 0, dif2 = 0;
double p = 0.0;
while(t > TF) //没有达到限定温度时
{
for (i = 0; i < N; i++)//循环次数
{
change();
dif1 = calprofit(a) - bestmaxp;
if (dif1 > 0)//若总利润比目标利润的大
{
for (j = 0 ; j < M; j++)
{
preseq[j] = a[j];
bestseq[j] = a[j];//把物品的取放赋予目标数组与先前一次数组
}
premaxp = calprofit(a);
bestmaxp = premaxp;//把当前利润赋予目标利润与先前一次利润
}
else
{
dif2 = calprofit(a) - premaxp;//把当前一次的利润与先前一次的利润做对比
if (dif2 > 0)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
p = rand()%20001/20000.0;
if (exp((dif2)/ t) > p)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
for (j = 0; j < M; j++)
{
a[j] = preseq[j];
}
}
}
}
}
t = t * T;//温度降低
}
}
int main(void)
{
int i;
srand((unsigned)time(NULL));
initialize();
SA();
for (i = 0; i < M; i++)
{
if (0 == (i % 5))
{
printf(" ");
}
printf("%d", bestseq[i]);
}
printf("\nThe best value is %d\n", bestmaxp);
return 1;
}
参考:
http://www.cnblogs.com/growing/archive/2010/12/16/1908255.html
http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html
http://blog.csdn.net/acdreamers/article/details/10019849
http://www.cnblogs.com/emanlee/archive/2011/07/31/2122727.html