
文档教程知识库【免费下载链接】cp-algorithmsAlgorithm and data structure articles for https://cp-algorithms.com (based on http://e-maxx.ru)项目地址https://gitcode.com/GitHub_Trending/cp/cp-algorithms点击查看免费下载模拟退火Simulated Annealing, SA是一种基于随机化的全局最优化算法它通过模拟金属退火过程中加热—冷却的物理机制以一定概率接受能量更高的状态从而跳出局部极小值、逼近全局最优解。本文以 CP-Algorithms 仓库的原创文章 src/num_methods/simulated_annealing.md 为骨架完整讲解 SA 的问题模型、能量函数、状态与邻居状态、概率接受函数、温度与衰减参数的选择方法并给出可直接复制运行的通用 C 模板与旅行商问题TSP完整实现。读完本文你将能够独立为任意离散组合优化问题设计状态转移、配置 $T$ 与 $u$ 参数并把 SA 作为竞赛中处理 NP 难题的实用武器。什么是模拟退火SA模拟退火Simulated Annealing, SA是一种随机化算法用于近似求解函数 $E(s)$ 的全局最优值。之所以称它为随机化算法是因为它在搜索过程中引入了一定量的随机性因此对同一输入多次运行得到的结果可能略有不同。SA 特别适合处理如下特征的问题状态空间是离散的能量函数 $E(s)$ 存在多个局部极小值local minima精确求解如动态规划、图论算法过于困难或代价过高。经典的例子就是旅行商问题Travelling Salesman Problem, TSP。问题模型以 TSP 为例给定二维平面上一组节点每个节点由其 $x$、$y$ 坐标刻画。任务是找到一个节点的排列顺序使得按照该顺序依次访问这些节点并返回起点所走过的总路程最短。TSP 是典型的 NP 难问题节点数量稍大就难以用精确算法在时限内求解而 SA 可以在可接受的时间内给出一个接近最优的解因此是练习模拟退火的理想载体。物理动机从金属退火到算法退火是一种冶金工艺把材料加热到高温然后让它缓慢冷却使内部原子重新排列到内能最小的构型从而使材料获得不同的物理性质。在这个类比中状态state是原子的排列方式内能internal energy是被最小化的目标函数原子的初始排列可以被看作是内能的一个局部极小值。要让材料重新排列原子就必须激励它越过一个内能并未达到最小的区域才能抵达全局极小值。这个激励正是通过加热到更高温度提供的。模拟退火算法字面意义上模拟了这一过程从一个随机状态材料出发设定一个高温加热在高温下算法愿意接受能量比当前状态更高的状态从而避免陷入局部极小值向全局极小值移动随着时间推移算法逐渐冷却开始拒绝更高能量的状态收缩到已找到的最近极小值附近。能量函数 $E(s)$$E(s)$ 是需要被最小化或最大化的函数它将每个状态映射为一个实数。在 TSP 中$E(s)$ 返回按状态 $s$ 中的节点顺序绕行一整圈所走过的总路程。状态State状态空间是能量函数 $E(s)$ 的定义域状态是状态空间中的任意一个元素。在 TSP 中所有能访问全部节点的可行路径共同构成状态空间其中任意一条路径就是一个状态。邻居状态Neighbouring state邻居状态是状态空间中与当前状态接近的另一个状态。通常我们可以通过一个简单变换从当前状态得到邻居状态。在 TSP 中一个邻居状态是这样得到的随机挑选两个节点在当前的排列中交换它们的位置。算法流程算法从随机状态 $s$ 开始。每一步执行如下操作为当前状态 $s$ 生成一个邻居状态 $s_{next}$若 $E(s_{next}) E(s)$则直接更新 $s s_{next}$否则调用概率接受函数 $P(E(s), E(s_{next}), T)$决定是移动到 $s_{next}$ 还是停留在 $s$在遍历所有迭代的同时始终记录历史最优状态$s_{best}$迭代持续到收敛或时间用尽为止。其中 $T$ 是温度初始设为较大值每一步以缓慢的速度衰减。温度越高算法越倾向于接受 $s_{next}$即使它的能量更高。初始化随机状态 s记录 s_best s 设置初始温度 T T0衰减率 u while T T_阈值: s_next 生成邻居(s) if P(E(s), E(s_next), T): s s_next if E(s) E(s_best): s_best s T * u 返回 (E(s_best), s_best)温度 $T$ 与衰减率 $u$温度 $T$量化算法接受更高能量状态的意愿。$T$ 越大算法越激进地探索越不容易陷入局部极小值。衰减率decay$u$一个常量量化算法的冷却速率。较慢的冷却速率更大的 $u$通常能得到更好的结果代价是运行时间更长。迭代次数设 $T_0$ 为初始温度$u$ 为衰减率循环终止条件为 $T 1$则总的迭代次数 $N$ 满足$$N \lceil -\log_{u}{T_0} \rceil$$因此总时间复杂度约为 $O(N \times cost_{迭代})$其中 $cost_{迭代}$ 是单次迭代中生成邻居 计算能量的开销。概率接受函数Probability Acceptance Function, PAFPAF 是模拟退火区别于普通爬山法的关键。定义如下$$P(E,E_{next},T) \begin{cases} \text{True} \quad\text{if } \mathcal{U}{[0,1]} \le \exp(-\frac{E{next}-E}{T}) \ \text{False} \quad\text{otherwise} \end{cases}$$其中 $\mathcal{U}{[0,1]}$ 是 $[0,1]$ 上的连续均匀随机数。该函数接收当前状态能量、下一个状态能量和温度返回一个布尔值告诉搜索过程是否移动到 $s{next}$。两个关键性质当 $E_{next} E$ 时$\exp(-\frac{E_{next}-E}{T}) 1$此时 PAF恒返回 True即能量下降的移动必定被接受当 $E_{next} \ge E$ 时仍以概率 $\exp(-\frac{E_{next}-E}{T})$ 接受该移动这对应物理学中的 Gibbs 测度。温度 $T$ 越高、能量差 $E_{next}-E$ 越小接受概率越大。C 实现使用 C11 及以上标准random与cmathbool P(double E,double E_next,double T,mt19937 rng){ double prob exp(-(E_next-E)/T); if(prob 1) return true; else{ bernoulli_distribution d(prob); return d(rng); } }说明当prob 1即 $E_{next} E$时直接返回true否则以prob为概率进行一次伯努利抽样。通用代码模板下面的模板来自仓库原文 src/num_methods/simulated_annealing.md使用者只需实现state类的三个方法即可将其用于任意离散优化问题。class state { public: state() { // Generate the initial state } state next() { state s_next; // Modify s_next to a random neighboring state return s_next; } double E() { // Implement the energy function here }; }; pairdouble, state simAnneal() { state s state(); state best s; double T 10000; // Initial temperature double u 0.995; // decay rate double E s.E(); double E_next; double E_best E; mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); while (T 1) { state next s.next(); E_next next.E(); if (P(E, E_next, T, rng)) { s next; if (E_next E_best) { best s; E_best E_next; } E E_next; } T * u; } return {E_best, best}; }使用方式在state()中生成初始状态在next()中基于当前状态做一次小扰动返回随机邻居状态在E()中实现能量函数调用simAnneal()返回{E_best, best}即历史最优能量与对应状态。参数设置$T$初始温度初始温度。希望搜索运行更久、探索更多状态时把 $T$ 设大。$u$衰减率决定冷却速率。更慢的冷却更大的 $u$通常效果更好但耗时更长。务必保证 $u 1$。循环迭代次数由下式给出$$N \lceil -\log_{u}{T} \rceil$$反过来已知 $N$ 也可以反推初始温度$$T u^{-N}$$选择 $T$ 与 $u$ 的实用建议如果局部极小值很多、状态空间很宽设置 $u 0.999$以慢速冷却换取更充分的探索如果状态空间较窄$u 0.99$ 通常就足够不确定时保守地取 $u 0.998$ 或更高先估算单次迭代的时间复杂度用 $N \lceil -\log_{u}{T} \rceil$ 推算一个不会超时TLE的迭代次数再用 $T u^{-N}$ 反推合适的初始温度。完整示例求解 TSP以 8 个二维坐标点为例的完整实现来自仓库原文 src/num_methods/simulated_annealing.mdclass state { public: vectorpairint, int points; std::mt19937 mt{ static_caststd::mt19937::result_type( std::chrono::steady_clock::now().time_since_epoch().count() ) }; state() { points {{0,0},{2,2},{0,2},{2,0},{0,1},{1,2},{2,1},{1,0}}; } state next() { state s_next; s_next.points points; uniform_int_distribution choose(0, points.size()-1); int a choose(mt); int b choose(mt); s_next.points[a].swap(s_next.points[b]); return s_next; } double euclidean(pairint, int a, pairint, int b) { return hypot(a.first - b.first, a.second - b.second); } double E() { double dist 0; int n points.size(); for (int i 0;i n; i) dist euclidean(points[i], points[(i1)%n]); return dist; }; }; int main() { pairdouble, state res; res simAnneal(); double E_best res.first; state best res.second; cout Length of shortest path found : E_best \n; cout Order of points in shortest path : \n; for(auto x: best.points) { cout x.first x.second \n; } }实现要点邻居生成随机选择两个下标a、b交换这两点在排列中的位置。这是 TSP 中典型且有效的2-opt 式扰动能量函数E()计算闭合回路总长即依次连接points[i]与points[(i1)%n]的欧氏距离之和用hypot计算两点间欧氏距离随机数用steady_clock时间戳作为mt19937的种子保证每次运行产生不同的搜索轨迹主程序调用通用simAnneal()输出最短路径长度和对应的节点顺序。对算法的进一步扩展仓库原文 src/num_methods/simulated_annealing.md 给出了几个实用的改造方向加入基于时间的退出条件在while循环中加入计时判断例如超过给定毫秒数即终止防止 TLE替换衰减函数上文实现的是指数衰减$T \leftarrow T \cdot u$。可以根据需要替换为任意衰减策略如线性衰减 $T \leftarrow T - \Delta T$调整 PAF 对能量差的敏感度默认 PAF 中指数分子包含 $E_{next} - E$因此偏好低能量状态。若希望 PAF 与能量差无关可直接删去该因子若希望加强/减弱能量差的影响可调整指数底数例如bool P(double E, double E_next, double T, mt19937 rng) { double e 2; // set e to any real number greater than 1 double prob pow(e,-(E_next-E)/T); if (prob 1) return true; else { bernoulli_distribution d(prob); return d(rng); } }注意此处指数底数e可以是任意大于 1 的实数底数越大接受能量上升状态的概率随能量差增长而衰减得越快。在 CP-Algorithms 仓库中的位置与验证方式本文对应的原始文章为Original原创文章front matter 标记tags: Original于 2025-05-21 收录于仓库更新日志见 README.md在站点导航中模拟退火位于 Numerical Methods Search 目录下与二分查找src/num_methods/binary_search.md、三分查找src/num_methods/ternary_search.md、牛顿法求根src/num_methods/roots_newton.md并列见 src/navigation.md定位是数值方法领域的全局搜索工具与传统确定性搜索互为补充仓库的测试体系test/extract_snippets.py、test/test.sh会从src/下带file属性的代码块自动提取测试片段并用g -stdc17编译运行。模拟退火一文中的代码块未带该属性属于需要读者结合模板自行适配的算法框架建议在提交自己的实现时用仓库提供的测试脚本验证编译与运行结果。练习题以下是仓库原文推荐的练习题目出自各大 OJ搜索题目标题即可找到原题USACO 2017 年 1 月金组 - Subsequence Reversal一个典型的状态置换 代价最小化问题适合用 SA 检验思路Deltix Summer 2021 - DIY Tree构造类优化问题可结合 SA 与图论算法设计状态转移AtCoder - Contest Scheduling著名的启发式求解练习题官方题解亦展示了退火式搜索的应用。实践建议先用模板解决小规模 TSP 验证正确性与收敛行为再逐步把问题规模放大并配合计时退出条件观察 $u$ 与 $T$ 对解质量和运行时间的影响。赞分享文档教程知识库【免费下载链接】cp-algorithmsAlgorithm and data structure articles for https://cp-algorithms.com (based on http://e-maxx.ru)项目地址https://gitcode.com/GitHub_Trending/cp/cp-algorithms点击查看免费下载相关推荐Swift 算法俱乐部模拟退火算法Simulated Annealing原理、Swift 实现与 TSP 实战Swift 算法俱乐部模拟退火算法Simulated Annealing原理、Swift 实现与 TSP 实战 模拟退火Simulated Anneal示例工程教程Cosmos 仓库旅行商问题TSP求解器实战基于模拟退火的 C 实现与源码解析Cosmos 仓库旅行商问题TSP求解器实战基于模拟退火的 C 实现与源码解析 导读 本文围绕 cosmos https://link.gitcode教程示例工程Ice用三个分区管好 macOS 菜单栏Ice用三个分区管好 macOS 菜单栏 菜单栏图标多到挤成一行、想用的却翻不到Ice 是一款免费开源的 macOS 菜单栏管理工具它把图标按可见、隐藏桌面应用上一篇**探索深度代码覆盖率测试kcov的强大魅力**下一篇clangd_extensions.nvim 使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考