模拟退火

模拟退火是常常用来解决最优化问题的算法 . 在 OI 竞赛中广泛应用 .

虽然不知道正解是啥 , 但我给你退个几万遍火绝对不怂你 .

算法梗概

爬山算法 不同的是 , 爬山每次会找一个能上升的点 , 然后快速向那边爬 , 然后多随机几个初始点去爬 .

退火 就是 后继状态虽然不一定比当前优秀 , 但我仍然有几率向那边走 .

它是模拟热力学退火过程的一个神奇的算法 , 将我们 目标函数 作为 能量函数 最后使得能量越来越低 .

后继状态比当前要优秀 , 那么直接继承这个后继状态 .

高温的时候我们大概率选择不优秀的后继状态 , 低温的时候我们小概率选择不优秀的后继状态 , 因为越低温越稳定 .

实现过程 : 初始高温 温度缓慢下降 终止在低温 (这时能量函数达到最小,目标函数最小)

算法实现

假设我们当前是使得最后的答案最小 .

那么每次我们是否继承后继状态是分两种选择的 .

我们假设本来状态的答案是 bef , 随机改变状态后的答案是 res , 当前温度为 T .

  1. res<bef , 直接继承 res .
  2. resbef , 我们以一定概率继承 就是 e|resbef|T(0,1] 也就是温度越高 继承概率越大 , 改变的越小 继承概率越大 . 反之则反 .

然后最后经常会获得一个很优秀的最终状态 .

代码实现

ans 为全局的最优答案 .

Possible() 返回 (0,1] 之间等概率随机的一个小数 . Init() 初始化 . Calc() 计算当前方案的答案 .

Change() 改变当前的方案 , Recover() 恢复之前的方案 .

有时候那个概率函数 exp 那里 , 容易写反 , 为了使得 ex1 那么我们强制使得 x0 就行了 .

这个参数 DeltaT,eps,T 是随便选择的 , 一般的话需要调参找到最优的参数 .

const int lim = 1e4; const double eps = 1e-7; int ans = 1e9; inline double Possible() { return rand() * 1.0 / RAND_MAX; } inline void Simulate_Anneal() { const double DeltaT = 0.99; int res = Init(); for (double T = 1e6; T > eps; T *= DeltaT) { int bef = res; Change(); res = Calc(); if (!(res < bef || exp(-fabs(res - bef) / T) > Possible())) res = bef, Recover(); ans = min(ans, res); } }

例题

BZOJ 3680 : 吊打XXX

n 个洞 , 每个洞坐标为 (xi,yi) 用完全弹性的绳子下面挂一个质量为 mi 的小球 .

然后所有绳子连向同一个绳结 . 忽略 摩擦 和 能量损失 (机械能守恒) .

球无限高 , 不会碰到地面 . (1n1000)

一开始莫名奇妙地想到了机械能守恒 , 然后列动能和重力势能的方程 , 最后可以动态分析每个点的速度 ?

再来个动量守恒 ? 好吧 ... 那样就很毒瘤了 ...

直接考虑共点力的平衡 , 对所有力进行正交分解 , 然后再合成到一起 . 然后如果最后合成力越小越优秀 .

然后直接模拟退火 (爬山也行 , 因为单峰) 每次随机一个方向走 . 注意一开始跑多点 , 不然要找到最优解很慢 .

我们可以一开始走温度 T 那么长的路 这样就行了 .

挂一个 Calc() 看一下正交分解 ...

#define sqr(x) ((x) * (x)) inline double Calc(double x, double y) { double sumx = 0.0, sumy = 0.0; For (i, 1, n) { double deltax = lis[i].x - x, deltay = lis[i].y - y; double len = sqrt(sqr(deltax) + sqr(deltay)); if (fabs(len) <= eps) continue ; sumx += lis[i].w * deltax / len; sumy += lis[i].w * deltay / len; } return sqrt(sqr(sumx) + sqr(sumy)); }

BZOJ 2428: [HAOI2006]均分数据

n 个正整数 a1...an . 现在将他们分成 m 组 , 使得每组数值和均方差最小 . 即

σ=i=1n(xix¯)2n,x¯=inxin

σ 为均方差 , x¯ 为各组数据和平均值 , xi 为第 i 组数据的数值和 .

mn20,2m6

这道题直接模拟退火就行了 , 似乎是模板题 ?

每次随机选择一个数 , 然后把他拿出来 , 然后贪心地放入当前权值最小的那一组中去 .

不然随便放的话就很找到的解就不优秀 . 基本上 100 次就可以退出最优解了 .


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9057871.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(475)  评论(3编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示