zoukankan      html  css  js  c++  java
  • 模拟退火

    啥是模拟退火

    本质上是一个欧皇算法

    模拟退火是一个随机算法,实际上就是优雅地碰运气。

    感觉它的原理其实和二分是差不多的,就是找出一个答案,看看答案是否更优。

    但是由于模拟退火并不局限于单调查找,所以它能适用于非单调的问题的求解。

    怎么搞模拟退火啊?

    我们看一道例题:P1337 [JSOI2004]平衡点 / 吊打XXX

    这道题要求找出系统最终的平衡位置。

    首先我们可以进行一波物理分析。

     因为系统总是朝着能量更低的状态变化的,于是我们知道,最终的平衡位置,肯定是使整个系统能量最低的位置。

    那么我们就可以随机猜测这个点的位置。

    这里我们利用物理中的退火来模拟这个猜测过程。

    算法流程


    模拟退火的原理和金属退火的原理近似:将热力学的理论套用到统计学上,将搜寻空间内每一点想像成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。演算法先以搜寻空间内一个任意点作起始:每一步先选择一个“邻居”,然后再计算从现有位置到达“邻居”的概率。


    这是百度百科上的解释,其实如果你对退火不了解根本看不懂。。

    我们直白地理解一下这个算法流程。

    一开始我们先随机确定一个初态。

    对这个初态,我们随机找到它的一个相邻状态。也就是直接rand一个数(要求取得到正负),然后之前取的数加上(这个数乘温度值)。

    找到它的相邻状态后,判断要不要选取相邻状态。

    首先发现,如果相邻状态比当前状态更优的话,肯定直接选了。

    如果相邻状态比当前状态不优的话,我们以一定的概率接受这个状态。

    那以怎样的概率接受呢?

    经过科学家的分析,这个概率大概就是$e^{frac{△}{T}}$,其中$△$是现在状态和最优状态的差值,$T$是当前的温度。

    从这个概率看,$△$越大,这个概率越大,当前温度越小,这个概率越大。

    感性地分析一下。

    当前温度大的时候,也就是刚开始查找的时候,我们是大范围跳动($rand()*T$很大)。这时候我们应该直接找更加优秀的状态。

    当前温度小的时候,我们就在之前找到的状态周边进行小范围跳跃,这时候我们就像亡命之徒一样,不顾一切碰运气想要找到一个更好的解。所以哪怕一个解比较差,我们也都暂时接受,期望能通过这个解跳出当前的峰,找到更高的峰。

    代码

    下面的代码我已经写好注释了,跟着注释看就可以理解了。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int read(){
     4     char c;int num,f=1;
     5     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
     6     while(c=getchar(), isdigit(c))num=num*10+c-'0';
     7     return f*num;
     8 }
     9 struct Hole{
    10     int x,y,w;
    11 }h[10009];
    12 //小孔 
    13 int n;
    14 double xans,yans,ans=1e18+7;
    15 const double delta=0.996;
    16 //降温系数 
    17 //洛谷上0.996能通过
    18 //BZOJ上0.973能通过 
    19 double ene(double x,double y){
    20     double sum=0;
    21     for(int i=1;i<=n;i++){
    22         double delx,dely;
    23         delx=x-h[i].x;
    24         dely=y-h[i].y;
    25         sum+=(sqrt(delx*delx+dely*dely)*h[i].w);
    26     }
    27     return sum;
    28 }
    29 //计算能量 
    30 void simulate_anneal(){
    31     double nx=xans,ny=yans,na;
    32     double t=2000;
    33     //初始位置和初始温度 
    34     while(t>=1e-14){
    35         double xtmp=nx+(rand()*2-RAND_MAX)*t;
    36         double ytmp=ny+(rand()*2-RAND_MAX)*t;
    37         //现在的坐标
    38         //rand()*2-RAND_MAX的作用是让随机数的取值变为(-RAND_MAX,RAND_MAX) 
    39         double nowa=ene(xtmp,ytmp);
    40         //现在的能量 
    41         double del=nowa-ans;
    42         //与最优解的能力差 
    43         if(del<0){
    44             xans=nx=xtmp;
    45             yans=ny=ytmp;
    46             ans=nowa;
    47             //找到更小的,直接取了 
    48         }else if(exp(-del/t)*RAND_MAX>rand()){
    49             nx=xtmp;
    50             ny=ytmp;
    51             //以一定概率接受当前能量 
    52         }
    53         t*=delta;
    54         //降温 
    55     }
    56 }
    57 void SA(){
    58     simulate_anneal();
    59     simulate_anneal();
    60     simulate_anneal();
    61     //进行多次模拟退火,取最优解 
    62 }
    63 int main()
    64 {
    65     srand(1926017);
    66     //随机数 
    67     n=read();
    68     for(int i=1;i<=n;i++){
    69         h[i].x=read();
    70         h[i].y=read();
    71         h[i].w=read();
    72     }
    73     SA();
    74     //模拟退火 
    75     printf("%.3f %.3f
    ",xans,yans);
    76     return 0;
    77 }
    View Code
  • 相关阅读:
    js替换问题replace和replaceAll
    CSS截取标题...
    MVC FormCollection collection
    IE和FF判断浏览器是否安装了flash插件
    Uploadify上传插件不兼容FF、Chrome等Netscape浏览器
    jquery错误消息: “Date”未定义,“String”未定义
    正则表达式取出指定字符窜之间的字符窜
    WPF 开发前学习(二)
    WPF 中的树应用
    WCF 数据传输遇到的问题
  • 原文地址:https://www.cnblogs.com/onglublog/p/10122420.html
Copyright © 2011-2022 走看看