模拟退火的一些个人见解

首先通过下面这个链接大概了解了一下什么是模拟退火,我觉得这篇博客还是不错的,但是最开始看完还是感觉很迷茫,不知道模拟退火改如何去应用并解决问题。

点击打开链接

为什么叫模拟退火呢?

想象一个高温物体的降温过程。其温度为T时出现能量差为dE的降温概率为P(dE) =  e ^ ( -dE / (k * T) ) 。

其实就是温度越高降温的概率越大,温度越低降温概率越小。而模拟退火就是利用这样一种思想去进行搜索。

那么在进行搜索的时候首先定义一个初始值( 温度 ) T , 一个系数 r ( 降温速度  0 < r < 1 ) , 假设你当前状态为 f i , 你的下一个状态为 f i +1 , 对这两个状态进行评价,如果更接近你想要的结果,就更新到这个状态,否则则以 P ( dE ) 的概率去更新到 这个状态,但是其实在实际题目中,这个概率是不必要的,这一步有时是可以忽略掉的。我们可以想象,随着搜索次数的不断增多,搜索范围将越来越趋近于稳定,也就是随着时间的增长温度降低的概率越来越低,直到趋近于1。对应搜索就是随着你搜索的次数越多,你搜索到的值是你想要的值的概率就越大。找了一张图可以直观的感受一下模拟退火的过程。

所以具体步骤

1.  初始温度 T , 精度 EPS ( 温度的下界 ) ,当T < EPS 时结束搜索,输出当前最优解

2.  在这个状态周围搜索出一个新的状态 , 如果是当前最优解则更新 ,如果不是则以降温概率更新 ( 可忽略 )

3.  缩小T.

对于初始温度T设定,初始值与你的搜索范围有关。因为T也决定你的搜索范围,为什么不是最优解以降温概率更新的部分可以忽略我是这样认为的,我觉得初始的搜索范围可以取整个搜索范围的大小,任意一个初始搜索点,每次搜索当前搜索范围内的状态,进行最优解的更新以及缩小搜索范围,根据模拟退火的思想,随着搜索范围 T ( 温度 )

的降低,最优解会越来越稳定,而我个人认为的取搜索范围,然后通过系数去减小搜索范围,这样就可以贪心的去保留当前最优解,最终依旧大概率获得全局最优解。可以忽略降温概率更新是因为依旧可以通过搜索范围的变化去得到一个更优值。最终当搜索范围小于你要求的精度时,输出当前最优解。

通过别人的一点认识(点击打开链接),对于精度要求为小数点后1位的,下界可以取1e-3或1e-4,降温系数取 0.8 ,对于精度要求为1e-5 , 下界可取1e-7或1e-8 ,降温系数取 0.98。 可以说是取精度要求* 1e-2 或 1e-3 为下界?然后降温系数在具体调试这样?而且根据实际情况降温系数取0.98或0.99的速度也还可以,这个我在下面的题目进行了小实验。


一.  一维坐标搜索

以hdu 2899为例

题意

F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)

F (x) 的最小值

我们可订初始温度 T 为 100 ,降温系数取 0.98 ,温度下界 ( 精度 )取1e-6 , 搜索起始点任意,取( 0 , 0 ) 。

题目很简单我就直接贴代码了。

#include<bits/stdc++.h>
using namespace std;
const double EPS=1e-6;
const double r=0.98;
const int dx[2]={-1,1};
double y;

double F(double x)
{
    return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>y;
        double step=100;
        double x=0;
        while(step>EPS)
        {
            for(int i=0;i<2;i++)
            {
                double next_x=x+dx[i]*step;
                if(next_x<0||next_x>100)
                    continue;
                if(F(next_x)<F(x))
                    x=next_x;
            }
            step*=r;
        }
        printf("%.4f\n",F(x));
    }
    return 0;
}


通过在hdu的提交,初始温度100,下界1e-6 , 退火速率 0.99时140ms,0.98时为78ms。


二. 二维坐标搜索

以hdu 3932 为例

题意:

给你一个搜索范围 ( 0,0 ) 到 ( X ,Y )这个矩形范围,给你 N 个点 , 要求找出一个点使这个点到这 N 个点的最大距离最小。

可以想象的就是把一维转变成二维,那么单点搜索我们就可以变成随机多点并行搜索,原来的双向移动,转变为360度随机移动。最后在多点的搜索结果中取最优。

显然移动是以圆的范围移动的,那么温度上界 T 取矩形对角线, 即圆的最大半径,精度为小数点后1位, 精确度EPS取1e-3, 降温速率 r 取0.8,对于每个点我随机了36次移动,具体这个值怎么取更合适我还没考虑清楚。代码如下。

#include<bits/stdc++.h>
using namespace std;

const double EPS=1e-3;
const double PI=acos(-1.0);
const double INF=0x3f3f3f3f;
const double r=0.8;
const int maxn=1e4+5;
const int num=20;

struct Point{
    double x,y;
};

Point a[maxn];
Point m[maxn];
int n;
double d[maxn];

double dist(Point A, Point B)
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

double MAX(Point t)
{
    double res=-1;
    for(int i=0;i<n;i++)
        res=max(res,dist(t,a[i]));
    return res;
}

int main()
{
    double xx,yy;
    srand(time(0));
    while(cin>>xx>>yy>>n)
    {
        for(int i=0;i<n;i++)
            cin>>a[i].x>>a[i].y;
        for(int i=0;i<num;i++)
        {
            m[i].x=(rand()%1000+1)/1000.0*xx;
            m[i].y=(rand()%1000+1)/1000.0*yy;
            d[i]=MAX(m[i]);
        }
        double T=sqrt(xx*xx+yy*yy);
        Point next,now;
        while(T>EPS)
        {
            for(int i=0;i<num; i++)
            {
                now.x=m[i].x;
                now.y=m[i].y;
                for(int j=0;j<36;j++)
                {
                    double ang=(rand()%1000+1)/1000.0*2*PI;
                    next.x=now.x+cos(ang)*T;
                    next.y=now.y+sin(ang)*T;
                    if(next.x<0||next.x>xx||next.y<0||next.y>yy)
                        continue;
                    if(MAX(next)<d[i])
                    {
                        m[i].x=next.x;
                        m[i].y=next.y;
                        d[i]=MAX(next);
                    }
                }
            }
            T*=r;
        }
        int res;
        double ans=INF;
        for(int i=0;i<num;i++)
        {
            if(d[i]<ans)
            {
                ans=d[i];
                res=i;
            }
        }
        printf("(%.1f,%.1f).\n",m[res].x,m[res].y);
        printf("%.1f\n",ans);
    }
    return 0;
}


三. 近似解决旅行商问题


留坑。。。

哪里有错误请指出!谢谢!





评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值