随机化算法属于省选芝士体系

0x01 前置芝士

你只需要会 rand 就可以啦!

当然如果你想理解的更透彻也可以先看看 爬山算法

0x02 关于退火

退火是一种金属热处理工艺,指的是将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却。目的是降低硬度,改善切削加工性;消除残余应力,稳定尺寸,减少变形与裂纹倾向;细化晶粒,调整组织,消除组织缺陷。准确的说,退火是一种对材料的热处理工艺,包括金属材料、非金属材料。而且新材料的退火目的也与传统金属退火存在异同。

也就是你可以理解为,金属在逐渐降温的过程中,它含的某种物质会趋于恒定。

换成 OI 的话说,如果每个温度对应一个答案,则随着温度越来越小,答案波动的范围就会越来越小,最后答案趋于恒定。例如下面这个经典图片,便是利用这个思想,最后得到了一个多峰函数的最值。模拟退火就是用于求解多峰函数的最值问题。

不过是不是看起来很玄学?现在我们来细化一下模拟退火思想。

0x03 模拟退火

你如果仔细看,刚刚那个图中答案好像和温度没有直接联系,也就是说答案是随机的。这个说法不太完全,答案和温度确实没有太大联系,但温度却可以限定答案所在的范围。

首先我们引入几个参数:当前最优解 \(A_0\),新的解 \(A\),上一个被接纳的解 \(A_1\),解的“变动量” \(ΔA\)(即 \(A\) 与 \(A_0\) 的差值,这里规定为 \(A - A_0\)),开始的温度 \(T_0\),当前温度 \(T\),最终的温度 \(T_{esp}\)。温度变动量 \(q\)。接纳:指对于一个解,我们认为它有可能是正确答案,或者说与正确答案有联系。

以下均以求多峰函数最大值为例来模拟整个过程。

首先在一开始,我们找到一个你认为接近正确答案的解 \(A_1\),将 \(A_0\) 初始置为 \(A_1\)。然后你需要在温度限制的范围内,去合理的再取到一个解,即 \(A\)。至于这个温度限制的范围其实很简单,你只需要求到一个满足条件的随机数即可。例如:你有一个横坐标,如果你要再求到一个横坐标,你就可以写成。

double cx = now.x + ((rand() << 1) - RAND_MAX) * t;
// RAND_MAX 是 <stdlib.h> 中的一个宏,也就是随机数的最大值。
// 通过 (rand << 1) - RAND_MAX 我们就得到了一个可能为正可能为负的随机数

这样的话,你就会发现随着 \(T\) 的不断减小,新的解与上一个解的差距就越小,即达到了我们的目的。

现在我们有了 \(A\),自然就可以求到所谓 \(ΔA\)。

得到 \(ΔA\)后:

  • \(ΔA > 0\) :也就是说当前解大于当前最优解,因为我们是求最大值,所以此时显然更新 \(A_0\),并更新 \(A_1 = A\)。

  • \(ΔA \leq 0\) :这就比较麻烦了,首先这样的情况是肯定不能更新 \(A_0\) 的。但我们考虑一下 \(A_1\),如果接纳 \(A\),那么我们下次就是从 \(A\) 开始扩展新的点,这很明显有可能不是最优的(你从多峰函数的一个峰的半山腰跌到了一个山谷)。但也有可能是更优的,因为你现在接纳的这个点,它可能不在最终答案的那一座峰上,所以你需要跳往另一座峰,也就是你需要接纳 \(A\)。这下该怎么办呢?有一个非常玄学的东西,叫:Metropolis接受准则。它告诉我们有一定概率去接纳 \(A\),而这个概率只需比较 \(\exp(-delta / t) * RAND\_MAX\) 和 \(rand()\) 的大小即可。

在做完这些操作后,我们让 \(T = T \times q\)。

好了,我们现在又有了一个 \(A_1\),这个 \(A_1\) 可能等于 \(A_0\) 也可能等于 \(A\)。我们将这个 \(A_1\) 再去执行上述操作,反复执行,随着温度的减小,我们就可以求到一个趋于恒定的答案了!

当然还有几个未处理的地方。

首先:

  1. \(T_0\) 的取值:根据题目而定,我通常使用 \(2000\) 到 \(5000\) 的一个整数。
  2. \(q\) 的取值:在前人的总结中,\(q\) 近似于 \(0.996\)。
  3. \(T_{esp}\) 的取值:这也需要视题目而定,如果这个值越小,按理说答案精度就越大,所以如果你不怕超时就可以开的小一点。

最后,关于时间复杂度,因为此算法使用了大量随机数,所以其时间复杂度近似于 \(O(rp)\)。

0x04 例题

LINK

一句话题意:给定 \(n\) 个坐标,求这 \(n\) 个坐标的 费马点

定义 \(sum\) 表示一个坐标到 \(n\) 个点的距离和。

首先,你会发现答案是一个函数,因为一个坐标只对应一个 \(sum\)。于是我们其实就是要求这个多峰函数 \(sum\) 的最小值。

那么就是模拟退火嘛。读者可以在根据下面这个代码理解一下实现。

值得注意的是,这道题函数 \(sum\) 的“横坐标”是一个坐标。

#include <bits/stdc++.h>
using namespace std; int read() {
int k = 1, x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
} const int MAXN = 5e3 + 5;
const double q = 0.996;
// 温度变动量
struct node {
double x, y;
node() {}
node(double X, double Y) {
x = X;
y = Y;
}
} a[MAXN], now, anst;
// now 初值为 A1,在这里是 (0, 0)
double ans = 1e18;
// 答案初值 int n;
double f(double x, double y) {
double ret = 0;
for (int i = 1; i <= n; i++) ret += sqrt((x - a[i].x) * (x - a[i].x) + (y - a[i].y) * (y - a[i].y));
return ret;
} void Make_Fire() {
double t = 3000;
while (t > 1e-16) {
double cx = now.x + ((rand() << 1) - RAND_MAX) * t;
double cy = now.y + ((rand() << 1) - RAND_MAX) * t;
// 求到 A 的横坐标,在这里是 (cx, cy)
double cnt = f(cx, cy);
// 求到 A
double delta = cnt - ans;
// 求到差
if (delta < 0) { // 如果差是小于 0 的
now = node(cx, cy);
// 接纳它
anst = node(cx, cy);
// 根据题目需要,更新答案的坐标
ans = cnt;
// 跟新答案
} else if (exp(-delta / t) * RAND_MAX > rand())
// 玄学接受准则
now = node(cx, cy);
// 接纳它
t *= q;
// 降温
}
} int main() {
srand(998244353);
n = read();
ans = 1e18;
for (int i = 1; i <= n; i++)
scanf("%lf %lf", &a[i].x, &a[i].y);
for (int i = 1; i <= 5; i++) Make_Fire();
printf("%.2lf %.2lf\n", anst.x, anst.y);
return 0;
}

Note -「模拟退火」的更多相关文章

  1. Note -「多项式」基础模板(FFT/NTT/多模 NTT)光速入门

      进阶篇戳这里. 目录 何为「多项式」 基本概念 系数表示法 & 点值表示法 傅里叶(Fourier)变换 概述 前置知识 - 复数 单位根 快速傅里叶正变换(FFT) 快速傅里叶逆变换(I ...

  2. Note -「群论」学习笔记

    目录 前置知识 群 置换 Burnside 引理与 Pólya 定理 概念引入 引例 轨道-稳定子(Orbit-Stabilizer)定理 证明 Burnside 引理 证明 Pólya 定理 证明 ...

  3. Note -「线性规划」学习笔记

    \(\mathcal{Definition}\)   线性规划(Linear Programming, LP)形式上是对如下问题的描述: \[\operatorname{maximize}~~~~z= ...

  4. Note -「计算几何」模板

      尚未完整测试,务必留意模板 bug! /* Clearink */ #include <cmath> #include <queue> #include <cstdi ...

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

    目录 题目链接 题解 代码 题目链接 loj#2076. 「JSOI2016」炸弹攻击 题解 模拟退火 退火时,由于答案比较小,但是温度比较高 所以在算exp时最好把相差的点数乘以一个常数让选取更差的 ...

  6. Note -「Lagrange 插值」学习笔记

    目录 问题引入 思考 Lagrange 插值法 插值过程 代码实现 实际应用 「洛谷 P4781」「模板」拉格朗日插值 「洛谷 P4463」calc 题意简述 数据规模 Solution Step 1 ...

  7. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  8. Note -「圆方树」学习笔记

    目录 圆方树的定义 圆方树的构造 实现 细节 圆方树的运用 「BZOJ 3331」压力 「洛谷 P4320」道路相遇 「APIO 2018」「洛谷 P4630」铁人两项 「CF 487E」Touris ...

  9. Note -「Dsu On Tree」学习笔记

    前置芝士 树连剖分及其思想,以及优化时间复杂度的原理. 讲个笑话这个东西其实和 Dsu(并查集)没什么关系. 算法本身 Dsu On Tree,一下简称 DOT,常用于解决子树间的信息合并问题. 其实 ...

随机推荐

  1. 【原创】记一次对X呼APP的渗透测试

    获取CMS并本地安装 X呼是一款开源的客服CMS系统,访问官网,下载安卓版本的app和源码本地搭建: 发现这cms预留admin表中的用户就不少.... 直接用预留的密码解密,然后就能登录手机APP了 ...

  2. drools中query的使用

    一.背景 我们知道在drools中是存在工作内存的,我们的Fact对象会加入到工作内存中,同时我们自己也可以在drl文件中使用insert/modify/update/delete等方法,修改工作内存 ...

  3. 组织:EFF

    电子前沿基金会(Electronic Frontier Foundation), 简称EFF,是一个非营利性的国际法律组织.该组织成立于1990年,创始人包括Mitch Kapor(Lotus公司的总 ...

  4. 146_ACCESS之HR招聘信息管理_64位

    焦棚子的文章目录 点击下载附件 一.背景: 最近把之前做的一个HR招聘信息管理工具翻新了下,有需要的朋友可以自取,主要想解决的问题是多人在跟进人员招聘的时候信息的不对称,这样下来的就可以及时的看到整个 ...

  5. python之装饰器补充与递归函数与二分查找

    目录 多层装饰器 有参装饰器 递归函数 基本演示 斐波那契数列 总结 小拓展 算法之二分法 简介 举例 总结 多层装饰器 我们已经知道了语法糖的作用是将装饰对象自动装饰到装饰器中,一个语法糖的应用我们 ...

  6. OI中组合数学公式和定理90%歼灭

    组合数学 基础概念 加法和乘法原理 加法原理 同一步下的不同选择,可以通过累加得到方案数. 乘法原理 整个流程的方案数可以由每一步的方案数相乘得到. 有了加法原理和乘法原理,就可以解决一些没有选择导致 ...

  7. Jackson多态序列化

    场景 做一个消息中心,专门负责发送消息.消息分为几种渠道,包括手机通知(Push).短信(SMS).邮件(Email),Websocket等渠道. 我定义了一个基类MessageRequest用来接收 ...

  8. 「Java分享客栈」Nacos配置中心称王称霸,我Apollo一生也不弱于人!

    前言 Apollo又称阿波罗配置中心,在前两年还是挺火的,但阿里SpringCloud套件席卷国内之后,nacos就成为了最被亲睐的分布式配置中心,nacos是配置中心和注册中心二合一的产品,单纯功能 ...

  9. np.r_、np.c_、np.concatenate和np.append

    np.r_是按行连接两个矩阵,就是把两矩阵上下相加,要求列数相等,最终结果的行数为两个矩阵行数和. np.c_是按列连接两个矩阵,就是把两矩阵左右相加,要求行数相等,最终结果的列数等于两矩阵的列数和. ...

  10. NOI Online 2022 一游

    NOI Online 2022 一游 TG 啊,上午比提高,根据去年的经验,题目配置估计那至少一黑 所以直接做 1 题即可.(确信) 总体:估分 140,炸了但没完全炸 奇怪的过程 开题:3 2 1 ...