【学习笔记】我命由天不由我之随机化庇佑 —— 爬山法 和 模拟退火法

article/2025/11/10 11:39:45

以下均假设最优解是在最低点。

爬山法

爬山算法是一种局部择优的方法,采用启发式方法,是对深度优先搜索的一种改进,它利用反馈信息帮助生成解的决策。

直白地讲,就是当目前无法直接到达最优解,但是可以判断两个解哪个更优的时候,根据一些反馈信息生成一个新的可能解。

因此,爬山算法每次在当前找到的最优方案 x x x 附近寻找一个新方案。如果这个新的解 x ′ x' x 更优,那么转移到 x ′ x' x,否则不变。

这种算法对于单峰函数显然可行。

Q:既然都知道是单峰函数了为什么不三分呢?

A:你说的对!直接三分好了。

B:我要是知道是单峰函数,我还在这里骗分???

爬山算法会引入『温度参数』 T T T

类比的说,爬山就是一个醉汉喝得狂醉然后再山上裸奔蹦迪。

他知道该回家了(不然就要跪搓衣板),然后每次会往他认为最低的地方狂奔过去(中途不刹车)。

显然他可能一次恰好奔到山脚然后下山回家,也有可能奔过了到另一座山顶上去了。

不过没关系,醉汉奔过头了还会存在奔回来的可能。

但显然这个过程很没用。仿佛醉汉回家全靠天公作美。

所以在暴走过程中,他会经受山顶寒风的摧残,酒精作用渐渐消减,他变得越发清晰。

他就变得谨慎一点,每次少奔一点以达到山脚最低点位置。

这就要引入『降温参数』来起到缓缓冷静的作用。

关于『降温参数』,一般是 [ 0.985 , 0.999 ] [0.985,0.999] [0.985,0.999] 中选,这样才能做到慢慢消减。

显然如果存在多个”转折“时,爬山就很容易进入局部最优解,而非全局最优解。

随着头脑逐渐清醒,醉汉蹦出局部最优解的期望就会更小,很有可能一直跳不过去某个坡。

在这里插入图片描述

模拟退火

为什么会有上面情况的产生呢?

其实是因为爬山法的写法,只有在新解优于当前解的时候,我们才会接受新解并移动到相应位置。

根据这种情况,我们知道其实偶尔新解不好我们也要去接受,这样才会存在跳出这个坡的可能。

这就是模拟退火了。

模拟退火相较于爬山就只是多了一个随机接受非最优解的部分,大大增加了找到全局最优解得可能。

以下是追根溯源,由物理和化学知识迁移到信息学上应用:

我们知道在分子和原子的世界中,能量越大,意味着分子和原子越不稳定,当能量越低时,原子越稳定。

『退火』是物理学术语,指对物体加温在冷却的过程。

模拟退火算法来源于晶体冷却的过程,如果固体不处于最低能量状态,给固体加热再冷却,随着温度缓慢下降,固体中的原子按照一定形状排列,形成高密度、低能量的有规则晶体(即全局最优解)。

而如果温度下降过快,可能导致原子缺少足够的时间排列成晶体的结构,结果产生了具有较高能量的非晶体(即局部最优解)。

因此就可以根据退火的过程,给其再增加一点能量,然后再冷却,如果增加能量,跳出了局部最优解,那么本次退火就是成功的。

模拟退火由两个部分组成: Metropolis \text{Metropolis} Metropolis 算法和退火过程。

Metropolis \text{Metropolis} Metropolis 算法就是如何在局部最优解的情况下让其跳出来,是退火的基础。

Metropolis \text{Metropolis} Metropolis 提出的以概率来接受新状态的重要性采样方法,而非使用完全确定的规则,称为 Metropolis \text{Metropolis} Metropolis 准则,计算量较低。

在这里插入图片描述

假设我们从 A A A 开始求解。先跳到 B B B,然后发现 B B B 更优,绝对接受。 B B B 又跳到 C C C,绝对接受。 C C C 跳到 D D D ,哎呀更差了呢!此时我们以一定的概率选择是否接受;假设接受, D D D 又跳到 E E E 发现比之前跳到的所有位置都优,直接接受, E E E 继续跳 … … \dots\dots ;不接受,就还停在 C C C 又开始随机下一个解。

至于这个接受概率是多少呢?已经有前人计算出来了。 x : x: x: 当前最优解, x ′ : x': x: 产生的新解
P = { 1 E ( x ′ ) < E ( x ) e − E ( x ′ ) − E ( x ) T E ( x ′ ) ≥ E ( x ) P=\begin{cases}1\quad\quad\quad\quad\quad\quad E(x')<E(x)\\e^{-\frac{E(x')-E(x)}{T}}\quad\quad\ E(x')\ge E(x)\end{cases} P={1E(x)<E(x)eTE(x)E(x) E(x)E(x)
从这个式子我们可以看出,如果新解更优,那么百分之百绝对接受;否则我们会以 P P P 的概率接受。

Metropolis \text{Metropolis} Metropolis 算法虽然说是模拟退火算法的基础,但直接使用的话可能导致寻找解得速度太慢,以至于无法过题。

为了确保在有限的时间收敛,必须设定控制算法收敛的参数。

在上面的公式中,可以调节的参数就是『温度参数』 T T T

  • T T T 如果过大,就会导致退火太快,迭代次数不够,最后停留在局部最优值就会结束迭代。
  • T T T 如果较小,则计算时间会增加。

实际应用中采用退火温度表,在退火初期采用较大的 T T T 值,随着退火的进行,逐步降低:

  • 初始的温度 T 0 T_0 T0 应选的足够高,使的所有转移状态都被接受。初始温度越高,获得高质量的解的概率越大,但耗费的时间也越长。

  • 退火速率。

    • 指数式下降: T n = λ T n , n = 1 , 2 , 3 , . . . T_n=\lambda T_n,n=1,2,3,... Tn=λTn,n=1,2,3,... λ \lambda λ 即是爬山算法里面的『降温参数』,选取的值同样遵守 < 1 <1 <1 又逼近 1 1 1 原则。

      这种方式最常见,且对每一温度,有足够的转移尝试,但其收敛速度比较慢。

    • 其它方式:

      • T n = T 0 log ⁡ ( 1 + t ) T_n=\frac{T_0}{\log(1+t)} Tn=log(1+t)T0
      • T n = T 0 1 + t T_n=\frac{T_0}{1+t} Tn=1+tT0
  • 终止温度。当 T 0 T_0 T0 迭代到终止温度时就会结束退火。一般设为 eps=1e-k k ∈ Z + k\in \Z^+ kZ+)。

一般针对数据进行『温度参数』 T T T,『降温参数』 Δ \Delta Δ 的调参,在本地手造大数据跑,自己抉择。

对时间和正确性的平衡选取。

有的时候可以用 #include <ctime> 库里面自带的 clock() 函数计时,也可以自己定一个跑的次数。

( clock() / (1.0 * CLOCKS_PER_SEC) ) <= TIME
//返回的是多少秒 所以TIME是个 <1 的浮点数

需要注意是:

  • 初始温度 T 0 T_0 T0 的选取对算法结果有一定的影响,最好是多次运行对结果进行综合判断。
  • 在算法运行初期,温度下降快,避免接受过多的差结果。当运行时间增加,温度下降减缓,以便于更快稳定结果。
  • 当迭代次数增加到一定次数时,结果可能已经达到稳定,但是距离算法结束还有一段时间。在设计程序时应该加入适当的输出条件,满足输出条件即可结束程序。

最后给出流程图总结:又淘了一张好图

img

显然,模拟退火也不一定是对的,这个概率接受就很概率。但对比爬山得到最优解的概率肯定是大大增加的。

例题

Strange function

hdu2899

T T T 次询问,每次给定 y y y

f ( x ) = 6 x 7 + 8 x 6 + 7 x 3 + 5 x 2 − y x ( 0 ≤ x ≤ 100 ) f(x)=6x^7+8x^6+7x^3+5x^2-yx\quad(0\le x\le 100) f(x)=6x7+8x6+7x3+5x2yx(0x100) 的最小值。

#include <bits/stdc++.h>
using namespace std;
double y;
const double eps = 1e-8; //终止温度
const double delta = 0.997; //温度变化率
mt19937 wwl(time(0));
uniform_real_distribution < double > range( 0, 100 );double f( double x ) {return 6 * pow(x, 7) + 8 * pow(x, 6) + 7 * pow(x, 3) + 5 * pow(x, 2) - y * x;
}double solve() {double T = 100, x = range( wwl ); //初始温度 以及初始随机解double now = f(x), ans = now;int Time = 8e5; //防止超时的卡点次数while( T > eps and Time -- ) { //要么降温完成 要么被卡时double tx = x + (rand() * 2 - RAND_MAX) * T; //随机扰动产生新解if( 0 <= tx and tx <= 100 ) {double nxt = f(tx);ans = min( ans, nxt );if( nxt < now ) //新状态更小 直接接受x = tx, now = nxt;else if( rand() < exp( ( now - nxt ) / T ) * RAND_MAX ) //在一定概率内仍接受这个解x = tx, now = nxt;}T *= delta; //降温}return ans;
}int main() {int T;scanf( "%d", &T );while( T -- ) {scanf( "%lf", &y );printf( "%.4f\n", solve() );}return 0;
}

[JSOI2004]平衡点 / 吊打XXX

洛谷链接

#include <bits/stdc++.h>
using namespace std;
#define delta 0.996
#define maxn 1005
#define eps 1e-15
struct node { int x, y, w; }g[maxn];
int n;double energy( double x, double y ) {double ans = 0, dx, dy;for( int i = 1;i <= n;i ++ ) {dx = x - g[i].x, dy = y - g[i].y;ans += sqrt( dx * dx + dy * dy ) * g[i].w;}return ans;
}double ansx, ansy, ans, x, y, now;
void Simulate_Anneal() {double T = 5e4;while( T > eps ) {double tx = x + (rand() * 2 - RAND_MAX) * T;double ty = y + (rand() * 2 - RAND_MAX) * T;double tw = energy( tx, ty );if( tw < ans ) ans = tw, ansx = tx, ansy = ty;if( tw < now ) x = tx, y = ty, now = tw;else if( rand() < exp( ( now - tw ) / T ) * RAND_MAX )x = tx, y = ty;T *= delta;}
}signed main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d %d %d", &g[i].x, &g[i].y, &g[i].w );for( int i = 1;i <= n;i ++ ) x += g[i].x, y += g[i].y;x /= n, y /= n;ansx = x, ansy = y;now = ans = energy( x, y );Simulate_Anneal();Simulate_Anneal();Simulate_Anneal();Simulate_Anneal();printf( "%.3f %.3f\n", ansx, ansy );return 0;
}

Haywire

洛谷链接

#include <bits/stdc++.h>
using namespace std;
#define TIME 0.9
#define eps 1e-10
#define delta 0.998
#define maxn 15
int n;
int p[maxn];
int f[maxn][5];int energy() {int ans = 0;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= 3;j ++ )ans += fabs( p[i] - p[f[i][j]] );return ans;
}int main() {mt19937 wwl(time(NULL));scanf( "%d", &n );uniform_int_distribution < int > id( 1, n );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= 3;j ++ )scanf( "%d", &f[i][j] );iota( p + 1, p + n + 1, 1 );int ans = energy();while( ( clock() / (1.0 * CLOCKS_PER_SEC) ) <= TIME ) {for( int i = 1;i <= n;i ++ ) p[i] = i;double T = 1e5; int x, y;while( T > eps ) {do { x = id( wwl ), y = id( wwl ); }while( x == y );swap( p[x], p[y] );int now = energy();if( now < ans ) ans = now;else if( rand() > exp( ( now - ans ) / T ) * RAND_MAX )swap( p[x], p[y] );T *= delta;}}printf( "%d\n", ans / 2 );return 0;
}

学习参考博客:

https://www.cnblogs.com/flashhu/p/8884132.html

https://blog.csdn.net/weixin_42398658/article/details/84031235

https://zhuanlan.zhihu.com/p/266874840?utm_source=qq


http://chatgpt.dhexx.cn/article/Givn5sQH.shtml

相关文章

人工智能-爬山法解决八数码问题-python源码

问题描述&#xff1a; 在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。这些数码可以在棋盘上移动&#xff0c;其移动规则是&#xff1a;与空格相邻的数码方格可以移入空格。现在的问题是&#xff1a;对于指定的初始棋局和目标棋局&#xff0c…

人工智能-爬山法解决八皇后问题-python源码

问题简述&#xff1a; 八皇后问题&#xff0c;一个古老而著名的问题&#xff0c;是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯贝瑟尔于 1848 年提出&#xff1a;在 88 格的国际象棋上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一…

树的搜索问题1(深度优先、广度优先,爬山法和best-first)

前言 我们在解决问题中经常使用到的数据结构一定少不了树&#xff0c;在数据结构这一大块中&#xff0c;我们对每一个结构都会讲各种形形色色的操作&#xff0c;比如栈的压栈出栈&#xff0c;树的各种遍历&#xff0c;但其实数据结构最重要的操作其实是搜索。如果我们不知道链…

TSP问题解决:模拟退火、贪心法、爬山法,Python实现

TSP问题解决&#xff1a;模拟退火、贪心法、爬山法&#xff0c;Python实现这里写目录标题 一、TSP问题二、简单介绍&#xff1a;贪心法、爬山法、模拟退火三、python代码实现四、分别用这三种方法得出结果&#xff0c;进行比较 一、TSP问题 1、TSP问题描述 简单来说&#xff0…

爬山法实现 八皇后问题 (Python 实现)

本文主要简单阐述爬山法的基本算法思想&#xff0c;并给出用此算法实现八皇后问题详细过程 最基本的爬上搜索算法表示&#xff1a;(节选自《人工智能》第二版)&#xff1a; function HILL-CLIMBING(problem) return a state thate is a locak maximum inputs: problem …

爬山法求解八皇后问题的全部解法

爬山法求解八皇后问题的全部解法 程序的概要设计思想初始状态冲突函数寻找邻居状态寻找全部解集 程序主要函数的作用运行结果截图Python源代码 程序的概要设计思想 爬山算法是一种局部贪婪算法&#xff0c;每次更新一次状态&#xff0c;都对相邻状态的冲突状态进行排序&#x…

Python启发式算法中爬山法的讲解及解方程问题实战(超详细 附源码)

一、启发式算法 还有一类重要的迭代法&#xff0c;它的迭代关系式不依赖问题的数学性能&#xff0c;而是受某种自然现象的启发而得到&#xff0c;称为启发式算法&#xff08;Heuristic Algorithm&#xff09;&#xff0c;如爬山法、遗传算法、模拟退火算法、蚁群算法等。 启发…

人工智能:爬山法、随机重启爬山法、模拟退火算法、遗传算法、启发式搜索方法解决八数码和八皇后问题

摘要 本文主要分为两个部分&#xff0c;分别采用实验对比对不同的方法进行分析。第一&#xff0c;以八数码问题和八皇后问题为例&#xff0c;对比爬山法&#xff0c;随机重启爬山法&#xff0c;模拟退火算法&#xff0c;遗传算法的搜索性能。第二&#xff0c;以八数码问题为例…

进化优化算法--第二章:爬山法

算法2.1&#xff1a; 最快上升爬山法 x0 <- 随机生成的个体 while not ( 终止准则)计算x0的适应度f(x0)For 每一个解的特征 q1,2,,...nxq <- x0 用一个随机变异替换xq的第q个特征计算xq的适应度f(xq)获取下一个更优的解:寻找使f(xq)最大的xq, 令其等于x, x <- argma…

Python:爬山法/随机重启爬山法/允许侧移的爬山法解决八皇后问题

文章目录 1 八皇后问题2 程序代码2.1 程序12.2 程序22.3 程序32.3.1 爬山法2.3.2 随机重启爬山法2.3.3 允许皇后侧移的爬山法 3 评价 1 八皇后问题 有一个8乘8的棋盘&#xff0c;现在要将八个皇后放到棋盘上&#xff0c;满足&#xff1a;对于每一个皇后&#xff0c;在自己所在…

爬山法和模拟退火

文章目录 前言一、爬山法1.算法步骤2.算法局限性 二、模拟退火1.算法步骤2.注意点3.例题求费马点保龄球均分数据 总结 前言 爬山法和模拟退火都为随机化算法&#xff0c;考场想不到正解时可用来骗分&#xff0c;通常效果较好。模拟退火是基于爬山法的优化。 一、爬山法 爬山法是…

搜索算法——爬山法

不断更新中...... 一、 爬山算法&#xff1a;爬山算法是一种简单的贪心搜索算法&#xff0c;该算法每次从当前位置的临近空间中选择一个最优解作为当前解&#xff0c;直到达到一个局部最优解。爬山算法可以类比成一个有失忆的人在浓雾中爬山。这里就揭示了爬山算法的两个问题…

搜索 —— 启发式搜索 —— 爬山法

【概述】 爬山法&#xff08;Hill Climbing&#xff0c;HC&#xff09;是一种局部择优的贪心搜索算法&#xff0c;其本质上是梯度下降法。 该算法每次从当前的节点开始&#xff0c;与周围的邻接点进行比较&#xff1a; 若当前节点是最大的&#xff0c;那么返回当前节点&…

Linux:快速查看IP地址及修改IP地址

导读Linux下如何快速查看IP地址及修改IP地址&#xff0c;有一个方法供参考 查ip 方法/步骤1: 打开linux操作系统在进入到界面 方法/步骤2: 在桌面右击打开终端。 方法/步骤3: 终端里输入ifconfig -a命令在回车键 方法/步骤4: 如下图可以看到了ip地址。 修改ip 方法/…

Linux Ubuntu查看IP信息的两种方式Ubuntu中检查你的 IP 地址

论使用什么系统&#xff0c;都有用到ip地址的时候&#xff0c;习惯了windows系统的人很容易就能查找出系统的ip&#xff0c;但是在linux系统如何查看ip呢&#xff1f;作为Linux新手&#xff0c;以Ubuntu的使用经验&#xff0c;我知道Ubuntu查看IP有两种方式。 第一种是在终端中…

Linux查询出口IP

查询的方式是通过Linux的curl访问查询ip的网站进行查询 具体步骤&#xff1a; 1.查询查询ip网站的ip 2.配置Linux的hosts文件 在/etc中的hosts文件增加上面的域名和ip&#xff08;注意&#xff1a;是ifconfig&#xff0c;不是ipconfig&#xff09; 3.在ssh命令下执行 curl ifc…

Linux 查看本地ip

Linux 查看本地ip 终端中输入指令ifconfig -a终端中输入指令hostname -I通过图形界面查看IP地址&#xff08;还未试通&#xff09; 终端中输入指令ifconfig -a 弹出的IP地址有点多 。 终端中输入指令hostname -I 会直接显示本机在局域网内的IP地址&#xff0c;但可能会显示多…

Linux服务器查看Ip地址

在服务器的网络配置中&#xff0c;需要同时配置这两种网络&#xff0c;才能使服务器正常使用。使用内网是为了保证我们的服务器处在一个安全的网络环境中&#xff0c;可以减少外部病毒的影响&#xff0c;而访问外网是为了方便我们配置服务器一些资源&#xff0c;如驱动程序&…

Linux 查看IP

UBuntu 系统下 按CtrlAltT 唤出终端 在终端输入: ifconfig 命令 点击回车 就可以看到自己电脑在局域网的IP地址了 图中第二行 inet 地址&#xff1a;192.168.1.101 就是你的电脑在局域网的IP地址了 如果输入 ifconfig 提示 找不到命令 那就在终端输入&#xff1a;sudo apt-get …

linux查看本机ip地址

linux中哪个命令可以查看自己的IP地址 50 我的主机是通过宽带联网的&#xff0c;主机的IP通过那个本地连接可以看到&#xff0c;但在虚拟机Debian linux5.0终端下输入route -n显示的是destination的IP&#xff0c;怎么查看属于虚拟机linux的IP呢&#xff1f;&#xff08;linux通…