模拟退火

Posted 中二病没有蛀牙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟退火相关的知识,希望对你有一定的参考价值。

玄学算法get√

模拟退火是一种通用的优化算法,其实学一下完全是因为数学建模也能用,在icpc中可以用于求一个毫无规律的函数最优解。当一个问题的方案数量极大(甚至是无穷的)而且不是一个单峰函数时,我们常使用模拟退火求解。

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

包括状态空间状态产生函数
(1).搜索空间(又叫状态空间)。一般范围比较大,事我们自定义的可行解的集合。

(2).状态函数。状态函数将决定你是否要选用当前的解,对于一个好的退火来说,状态函数的搜索空间应该足够大。

(3).候选解。一般采用随机数来在一定密度内随机选取。

(4).概率分布。大多采取均匀分布或指数分布。

状态转移概率
(1).状态转移概率是指从一个状态向另一个状态的转移概率。

(2).通俗的理解是接受一个新解为当前解的概率。

(3).它与当前的温度参数T有关,随温度下降而减小。

(4).一般采用Metropolis准则。

过程
模拟退火的过程中维护T,另包含三个参数:初始温度 T 0 T_0 T0,降温系数 Δ \\Delta Δ,中止温度 T k T_k Tk

T 0 T_0 T0 是一个比较大的数, Δ \\Delta Δ是一个略小于1的正数, T k T_k Tk是一个略大于 0 的正数。

​ 先让温度 T = T 0 T=T_0 T=T0,然后每次降温时 T = T ⋅ Δ T=T·\\Delta T=TΔ,直到 T ≤ T k T≤T_k TTk为止。此时当前接为最优解。

注意事项
温度T的初始值设置问题。 初始温度高,则搜索到全局最优解的可能性大,但因此要花费大量的计算时间;反之,则可节约计算时间,但全局搜索性能可能受到影响。

退火速度问题。 模拟退火算法的全局搜索性能也与退火速度密切相关。同一温度下的“充分”搜索(退火)是相当必要的,但这需要计算时间。

例题

https://www.luogu.com.cn/problem/P1337

这个题目就是求解重力势能最小的时候的点
显然模拟退火并不适合运气不好的人,提交了25发才ac罢了在这里插入图片描述

加粗样式

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
#define down 0.996

int n;
double ansx,ansy,answ;
struct node {
    int xi,yi,wi;
}a[maxn];

double cal(double x,double y)
{
    double ans = 0.0,dx,dy;
    for(int i = 1;i <= n;++i){
        dx = x - a[i].xi;
        dy = y - a[i].yi;
        ans += sqrt(dx * dx + dy * dy) * a[i].wi;
    }
    return ans;
}

void sa()
{
    double t =2000;
    while(t > 1e-17)
    {
        double ex = ansx + (rand() * 2 - RAND_MAX ) * t;
        double ey = ansy + (rand() * 2 - RAND_MAX ) * t;
        double ew = cal(ex,ey);
        double de = ew -answ;
        if(ew - answ < 0){
            ansx = ex;
            ansy = ey;
            answ = ew;
        }
        else if(exp(-de / t) * RAND_MAX > rand()){
            ansx = ex;
            ansy = ey;
        }
        t *= down;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n; i++){
        scanf("%d%d%d",&a[i].xi,&a[i].yi,&a[i].wi);
        ansx += a[i].xi;
        ansy += a[i].yi;
    }
    ansx /= n;
    ansy /= n;
    answ = cal(ansx,ansy);
    sa();
    sa();
    sa();
    sa();
    sa();
    printf("%.3lf %.3lf\\n",ansx,ansy);
    return 0;
}

以上是关于模拟退火的主要内容,如果未能解决你的问题,请参考以下文章

Matlab 模拟退火算法模型代码

loj#2076. 「JSOI2016」炸弹攻击 模拟退火

C++2018华为软挑:模拟退火+贪心FF解决装箱问题

模拟退火板子

模拟退火入门——求解TSP和洛谷P2210

模拟退火解TSP问题MATLAB代码