浅谈模拟退火

蒟蒻最近学习了模拟退火,与各位巨佬来分享一下自己的见解,不足之处望多指教

浅谈模拟退火

Part 1:何为模拟退火

首先,模拟退火=模拟 退火,它本质上是对冶金学中退火过程的一种模拟.

在某些问题中,我们可以把问题抽象成一个函数.这个函数的最大/最小值就是问题的解

有些问题很美妙,它在问题区间内是单峰函数,直接贪心/三分即可(问题都成这样了那还要模拟退火有什么用呢?)

但是有些问题比较鬼畜,它不是单峰函数,单纯的贪心算法很容易陷入局部最优解而忽略了全局最优解

那么解决办法,也就是模拟退火的思想:在求得一个新解时,如果比当前解优,那就采用新解(这个毋庸置疑)

关键是:

如果新解不如当前解,以一定的概率接受它,放弃局部最优解但是却有可能得到全局最优解

Part 2:模拟退火实现

上面所讲的模拟退火的思想相信大家都理解到了,那么模拟退火究竟如何实现呢?

按照上面所说,以一定的概率接受更"差"的解.但是这个概率是多少呢?

如果概率小了,那么你就有可能陷在局部最优解出不去了

如果概率大了,那么你就有可能放弃了好不容易找到的全局最优解

那么在模拟退火算法中,接受更"差"的解的概率随着温度的降低而降低

那么我们引入温度的概念:

$T$表示当前温度

$T_{s}$表示初始温度

$T_{t}$表示结束温度

$\Delta t \in {x;|;x\in R,0 < x < 1}$表示降温系数,每次用$T = T\Delta t$来模拟降温这个过程

那么我们先让$T = T_{s}$,每次执行$T = T\Delta t$,直到$T \leq T_{t}$为止

如果求得了一个新解,比当前解优则接受(模拟退火本质是贪心),如果不如当前解以$e^{\frac{\Delta E}{kT}} $的概率接受它,$\Delta E$为新解与当前解的差值,$T$为当前温度,$k$为随机数

上图来自Wiki,从上图可以看出,随着温度的降低,解逐渐稳定下来,并集中在最优解附近

那么如何生成新解呢?

随机生成:

  • 坐标系问题:对于坐标系问题,我们可以随机生成一个点
  • 序列问题:$random_shuffle$或者随机交换两个数

一遍$SA$多半跑不出最优解,如果您是欧皇当我没说,因此我们可以多跑几遍$SA$取最优

SA本质还是贪心,复杂度低到$O(n)$级别,因此跑几百次$SA$时间都可以承受

更何况我们还有$ctime$库呢,可以在时间允许的情况下一直跑$SA$

Part 3:模拟退火细节

Q:随机数种子咋办?

A:$srand(time(NULL))$,或者东方某神秘八位质数

Q:总是WA?

A:可能是您太非了,也许洗把脸就AC了

当然,也可以更换随机数种子 调大$\Delta$ 调大$T_{s}$ 调小$T_{t}$ 多跑几遍SA

Q:交了两页交不过:

A:您大可以打正解

Q:时间复杂度?

A:玄学

Part 4:模拟退火实战

我们以[NOIP2017]宝藏为例

这题实际上是个序列问题,即要求打通的顺序使总代价最小

然后可以套板子了

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 16;
int g[maxn][maxn],n,m;
struct Node{
    int d[maxn],deep[maxn];
    Node(){
        for(int i = 1;i <= n;i++)
            d[i] = i,deep[i] = 0;
    }
    Node(const Node &rhs){
        memcpy(d,rhs.d,sizeof(d));
        memset(deep,0,sizeof(deep));
    }
    Node operator = (const Node &rhs){
        memcpy(d,rhs.d,sizeof(d));
        memset(deep,0,sizeof(deep));
        return *this;
    }
    inline int solve(){//按照打通顺序求代价
        int ret = 0;
        deep[d[1]] = 1;//第一个打通的节点深度为1
        for(int i = 2;i <= n;i++){
            int temp = 0x7fffffff;
            for(int j = 1;j < i;j++){//枚举由哪一个已经打通的节点打通道路
                if(g[d[i]][d[j]] != 0x3f3f3f3f && deep[d[j]] * g[d[i]][d[j]] < temp)
                    temp = deep[d[j]] * g[d[i]][d[j]],deep[d[i]] = deep[d[j]] + 1;
            }
            if(temp == 0x7fffffff)return 0x7fffffff;//当前方案不可行可以提前退出了
            ret += temp;
        }
        return ret;
    }
};
inline int SA{//SA
    const double max_temp = 10000.0;//初始温度
    const double delta_temp = 0.98;//降温系数
    double temp = max_temp;//当前温度
    Node path;//打通顺序
    while(temp > 0.1){
        Node tpath(path);
        swap(tpath.d[rand() % n + 1],tpath.d[rand() % n + 1]);//随机一个新解
        double delta = tpath.solve() - path.solve();//求解
        if(delta < 0)path = tpath;//如果新解更优,则接受
        else if(exp(-delta / temp) * RAND_MAX >= rand())path = tpath;//否则以一定概率接受
        temp *= delta_temp;
    }
    return path.solve();
}
int main(){
    srand(19260817);//东方神秘质数
    memset(g,0x3f,sizeof(g));
    scanf("%d %d",&n,&m);
    for(int u,v,d,i = 1;i <= m;i++){
        scanf("%d %d %d",&u,&v,&d);
        g[u][v] = min(g[u][v],d);
        g[v][u] = min(g[v][u],d);
    }//存图
    int ans = 0x7fffffff;
    for(int i = 1;i <= 233;i++)//跑SA,取最优值
        ans = min(ans,SA());
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/colazcy/p/10294011.html

时间: 2024-11-06 07:27:49

浅谈模拟退火的相关文章

浅谈欧洲算法——模拟退火

初听说退火这个名词感觉就很(zhuang)帅(A__CDEFG...) 直到学了退火之后,我才发现: 退火不只是帅,而且非常万能 甚至比 D (大) F (法) S (师)还要万能 简直就是骗(de)分神器啊 简介 作为一个计算机算法,它竟然在百度上有物理词条! 当时我看了就懵了,你说计算机一个算法,跟冶炼金属有什么关系啊? 后来我看了算法的词条... 是不是更懵了... 方便大家理解(变得更懵),我搬了百度上的定义: Simulate Anneal Arithmetic (SAA,模拟退火算法

.net中对象序列化技术浅谈

.net中对象序列化技术浅谈 2009-03-11 阅读2756评论2 序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数 据.例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象.反之,反序列化根据流重新构造对象.此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件 中“恢复”对象到序列化之前的状态.在.net中有提供了几种序列化的方式:二进制序列化

浅谈——页面静态化

现在互联网发展越来越迅速,对网站的性能要求越来越高,也就是如何应对高并发量.像12306需要应付上亿人同时来抢票,淘宝双十一--所以,如何提高网站的性能,是做网站都需要考虑的. 首先网站性能优化的方面有很多:1,使用缓存,最传统的一级二级缓存:2,将服务和数据库分开,使用不同的服务器,分工更加明确,效率更加高:3,分布式,提供多台服务器,利用反向代理服务器nginx进行反向代理,将请求分散开来:4,数据库的读写分离,不同的数据库,将读操作和写操作分开,并实时同步即可:5,分布式缓存,使用memc

单页应用SEO浅谈

单页应用SEO浅谈 前言 单页应用(Single Page Application)越来越受web开发者欢迎,单页应用的体验可以模拟原生应用,一次开发,多端兼容.单页应用并不是一个全新发明的技术,而是随着互联网的发展,满足用户体验的一种综合技术. SEO 一直以来,搜索引擎优化(SEO)是开发者容易忽略的部分.SEO是针对搜索(Google.百度.雅虎搜索等)在技术细节上的优化,例如语义.搜索关键词与内容相关性.收录量.搜索排名等.SEO也是同行.市场竞争常用的的营销手段.Google.百度的搜

浅谈html标签

浅谈html各常用标签用法 标题标签:<h1>-<h6>来表示,使标题字体变粗. <br />换行标记 <hr />水平分隔符 &nbsp空格符 &copy版权符 <a href>a标签超链接 href可接链接地址 <p>段落标签<blockquote>引用标签及可用做缩进 <table>表格中的<ul>无序列表<ol>有序列表<dl>自定义列表<row

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

[nRF51822] 14、浅谈蓝牙低功耗(BLE)的几种常见的应用场景及架构(科普类干货)

蓝牙在短距离无线通信领域占据举足轻重的地位—— 从手机.平板.PC到车载设备, 到耳机.游戏手柄.音响.电视, 再到手环.电子秤.智能医疗器械(血糖仪.数字血压计.血气计.数字脉搏/心率监视器.数字体温计.耳温枪.皮肤水分计等), 再到智能家居等领域均占有一席之地. 而蓝牙低功耗(BLE)是在蓝牙4.0协议上修改以适用低功耗应用场景的一种蓝牙协议. 随着上一股智能消费类电子大潮的到来,BLE的各种应用也像雨后春笋般在市场上铺开. 如果想 紧跟蓝牙协议的最新动态 ,可以在https://www.b

浅谈C++容器动态内存管理的优化

在信息学竞赛中,C++的容器的用途非常广泛,但经常因常数过大而超时.怎样才能提高它们的效率呢? 我们知道,容器是存储同一类对象的对象,既然"对象"我们无法改变,那么我们只能从"存储"入手,不难想到,不同容器在实现上的根本区别是它们对应着不同的内存组织方式,内存管理无疑是这种实现的核心,所以优化内存管理是加快容器效率的最好途径之一. 一.内存分配器简介 怎样才能优化内存管理呢?很简单,C++为我们提供了这样的接口,我们可以通过自定义容器模板中的最后一个allocato

张小龙浅谈微信公众平台的意义

腾讯高级副总裁张小龙表示:微信公众平台,就是在移动互联网时代,让企业和个人以更简捷的形式提供服务给有需要的人. 张小龙浅谈微信公众平台的意义,布布扣,bubuko.com