现代启发式算法--初步了解模拟退火算法

模拟退火算法(搜索过程引入随机因素的贪心法,具有全局优化的性能)

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

    原文作者:启发式算法
    原文地址: https://blog.csdn.net/thj19980720/article/details/63262548
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞