zoukankan      html  css  js  c++  java
  • AcWing算法进阶课 搜索 模拟退火法

    涉及到最优化问题都可以尝试使用模拟退火法,比如DP问题、计算几何问题、贪心问题。

    模拟退火法是一个经过优化的随机化算法,有很大概率找到最优解。

    在使用这个算法的时候,题目应该具有一定的连续性。(函数的连续性比较强,有多峰的话,都可以尝试一下)

    温度(步长) : 初始温度(由题目的搜索空间范围确定),终止温度(温度降到什么时候程序会停下来),衰减系数(取一个比较接近1的从0到1的小数,采用指数型衰减,这个参数需要自己手调)

    每次随机的时候,从当前点开始,向它一个区域内随机。温度越大,我们所考虑的当前点的邻域(范围)就越大,

    温度是一个不断降低的过程,每次随机的区间是不断缩小的,最终会固定住点的位置。

     以取最小值点为例:

    随机选择一个点:f(新点) - f(当前点) = ΔE

    情况1:ΔE < 0 ,则跳到新点(能量值降低了,意味着当前点一定不是最低点,当前点一定不可选,则跳到新点)

    情况2:ΔE > 0, 则以一定概率跳到新点,同时ΔE的值越小,跳过去的概率就越大,Δ的值越大,跳过去的概率就越小。

    (如果Δ大于0,说明我们找的新点的能量值大于当前点,没有当前点能量值低,但是考虑到存在多个极值点,而我们找的是最小值,所以,我们以一定的概率跳到新的点,如果ΔE的值越小,说明两个点的能量值越接近,那么我们跳到新点的概率就越大,如果新点的能量比当前点的能量大了很多的话,那基本上就不需要跳过去了,也就是以很小的概率跳过去,取值的经验为:e^(-ΔE/T)(如果ΔE的值小于0,那么概率就是一个大于1的数,可以直接跳过去,若ΔE的值大于0,那么我们的取值就是一个0~1之间的数,而且如果E越大的话,我们的取值就会越小。

    我们在操作的过程中,温度会不断降低,那么每次随机的区域就会越来越小,温度越低,同时往外乱跳的概率就会越小。最终,当我们的温度降到了终止温度的时候,程序就可以停下来了。

    模拟退火法可以解决连续性较强的函数多峰问题的,当然也有可能最后只收敛到了一个局部最优解,为了降低收敛到局部最优解的概率,模拟退火整个的过程一般会迭代若干次,比如做100次,做100次意思是我们随机100个初值点。

    模拟退火法流程:

    void simulate_anneal()
    {
      随意一个初值点
      for (T = 初始温度; T > 终止温度; T = T * 终止系数)
      {
        在当前点周围随机一个点
        Δ = f(新点) - f(当前点);
        if () 
      }
    }
    

    模拟退火法的常用的一些技巧:

    模拟退火运行时间比较随机,不太好估计,我们可以使用卡时的方法

    while (clock() < 0.8)

    如果TLE的话,可以调节终止温度,和衰减系数

    #include <bits/stdc++.h>
    #define x first
    #define y second
    using namespace std;
    typedef pair<double, double> PDD;
    const int N = 110;
    int n;
    PDD q[N];
    double ans = 1e9;
    //随机一个从l到r的浮点数的函数
    double rand(double l, double r)
    {
    //随机数除以随机数的最大值,得到0到1的数,然后乘上步长加上左端点得到l到r的一个随机数
    return (double)rand() / RAND_MAX * (r - l) + l;
    }
    double get_dist(PDD a, PDD b)
    {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
    }
    //求一下全局的总的距离和是多少
    double calc(PDD p)
    {
    double res = 0;
    for (int i = 0; i < n; i ++ )
    res += get_dist(p, q[i]);
    ans = min(ans, res);
    return res;
    }
    void simulate_anneal()
    {
    PDD cur(rand(0, 10000), rand(0, 10000));
    for (double t = 10000; t >= 1e-4; t = t * 0.996)
    {
    //在当前点内随机一个新点
    PDD np(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t));
    double delta = calc(np) - calc(cur);
    if (exp(-delta / t) > rand(0, 1)) cur = np;
    }
    }
    int main()
    {
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%lf%lf", &q[i].x, &q[i].y);
    for (int i = 0; i < 100; i ++ ) simulate_anneal();
    printf("%.0lf
    ", ans);
    return 0;
    }
    

      

  • 相关阅读:
    【正则表达式】正则表达式基础语法
    【JavaWeb】实现二级联动菜单
    【JavaWeb】jQuery对Ajax的支持
    MySQL复习值代码知识点(2)
    easyUI+servlet+mysql项目总结
    Android环境配置(Eclipse全开发环境下载)
    jsp+servlet+mysql简单实现用户登陆注册
    java的异常抛出throws和throw的简单使用
    关于Java的多线程Runnable的个人理解(基础,不讲概念)
    Eclipse中代码自动添加注释及代码注释模板
  • 原文地址:https://www.cnblogs.com/Iamcookieandyou/p/14901479.html
Copyright © 2011-2022 走看看