模拟退火算法(Simulated Annealing, SA)专业教程(增强版)
1. 背景溯源:从物理退火到算法灵感
1.1 物理原型:固体的退火过程
模拟退火算法的核心灵感来自固体热力学中的退火现象 。固体的退火过程是一个非平衡态向平衡态过渡的热力学过程,可分为三个阶段:
- 加热熔化 :将固体加热至熔化温度以上(使分子动能远大于分子间势能),固体内部粒子从有序的晶体结构转变为无序的热运动状态;
- 缓慢冷却 :以极慢的速率降低温度(如每小时降温1-10K),使粒子有足够时间在每个温度下重新排列,逐渐从无序向有序过渡;
- 晶体形成 :当温度降至室温以下时,粒子最终排列成能量最低的晶体结构(全局稳定状态)。
若冷却速率过快(如淬火),粒子来不及调整到低能量位置,会形成非晶体(能量较高的亚稳定状态,即局部最优)。
1.2 算法与物理过程的对应关系
模拟退火算法直接模拟固体退火的热力学过程,将优化问题映射为"粒子系统的能量最小化问题"。下表是更细致的对应关系:
| 物理过程 | 算法对应概念 | 数学表示 | 含义解释 |
|---|---|---|---|
| 固体的粒子状态 | 优化问题的解状态 | x \in S | 解空间 S 中的一个元素(如 TSP 中的城市排列、函数优化中的变量取值) |
| 粒子的势能 | 目标函数值 | f(x) | 解 x 的"优劣程度"(最小化问题中,f(x) 越小越优) |
| 温度 T | 算法的"随机扰动强度" | T(t) | 温度越高,算法越倾向于接受劣解(探索解空间);温度越低,越倾向于保留优解(利用已有解) |
| 缓慢冷却 | 降温策略 | T(t+1) = g(T(t)) | 控制温度随迭代逐步降低的规则,需满足"足够慢"的要求 |
| 晶体的最低能量状态 | 全局最优解 | x^* = \arg\min_{x \in S} f(x) | 目标函数 f(x) 的最小值(或最大值) |
| 粒子在恒温下的热平衡 | 算法在温度 T 下的状态分布 | P_T(x) | 温度 T 时解 x 被访问的概率,服从玻尔兹曼分布 |
2. 核心思想:概率性跳出局部最优
2.1 对贪心算法的突破
传统贪心算法(如局部搜索、梯度下降)的核心规则是仅接受更优解 (若新解 x′x'x′ 满足 f(x′)<f(x)f(x') < f(x)f(x′)<f(x),则接受 x′x'x′;否则拒绝)。这种策略的致命缺陷是:一旦陷入局部最优解(即邻域内无更优解),算法无法继续优化。
模拟退火的核心创新在于:以一定概率接受劣解 (当 f(x′)>f(x)f(x') > f(x)f(x′)>f(x) 时,仍有机会接受 x′x'x′)。这种概率性策略使算法能"跳出"局部最优区域,探索更广阔的解空间,最终收敛到全局最优。
2.2 Metropolis准则:概率接受的理论基石
概率接受劣解的规则并非随意设计,而是来自统计力学中的Metropolis准则 (1953年由Metropolis等提出)。其本质是模拟正则系综 (恒温、封闭、体积固定的热力学系统)的热平衡过程,确保系统在每个温度下都能达到玻尔兹曼分布(能量越低的状态出现概率越高)。
3. 核心理论与公式推导
3.1 玻尔兹曼分布:热力学平衡的概率基础
在统计力学中,正则系综的状态概率服从玻尔兹曼分布 :PT(x)=1Z(T)exp(−f(x)kT) P_T(x) = \frac{1}{Z(T)} \exp\left(-\frac{f(x)}{kT}\right) PT(x)=Z(T)1exp(−kTf(x))
其中:
- Z(T)=∑x∈Sexp(−f(x)kT)Z(T) = \sum_{x \in S} \exp\left(-\frac{f(x)}{kT}\right)Z(T)=∑x∈Sexp(−kTf(x)) 为配分函数(归一化常数,确保所有状态的概率和为1);
- kkk 为玻尔兹曼常数 (物理中约 1.38×10−23 J/K1.38 \times 10^{-23} \, \text{J/K}1.38×10−23J/K,算法中常取 k=1k=1k=1 以简化计算);
- TTT 为当前温度;
- f(x)f(x)f(x) 为解 xxx 的目标函数值(对应粒子的势能)。
玻尔兹曼分布的核心性质:
- 温度越高 :exp(−f(x)/(kT))\exp(-f(x)/(kT))exp(−f(x)/(kT)) 越接近1,各状态的概率越均匀(算法倾向于探索未知解空间);
- 温度越低 :exp(−f(x)/(kT))\exp(-f(x)/(kT))exp(−f(x)/(kT)) 对低能量状态越敏感,低能量状态的概率占优(算法倾向于利用已有优解);
- 温度趋近于0:概率集中在能量最低的状态(算法收敛到全局最优解)。
3.2 Metropolis准则的推导:细致平衡条件
为确保算法在温度 TTT 下能达到玻尔兹曼分布,需满足细致平衡条件 (Detailed Balance Condition):PT(x)⋅Q(x→x′)⋅A(x→x′)=PT(x′)⋅Q(x′→x)⋅A(x′→x) P_T(x) \cdot Q(x \to x') \cdot A(x \to x') = P_T(x') \cdot Q(x' \to x) \cdot A(x' \to x) PT(x)⋅Q(x→x′)⋅A(x→x′)=PT(x′)⋅Q(x′→x)⋅A(x′→x)
其中:
- Q(x→x′)Q(x \to x')Q(x→x′) 为转移概率 (从解 xxx 生成邻域解 x′x'x′ 的概率,由邻域结构决定);
- A(x→x′)A(x \to x')A(x→x′) 为接受概率 (生成 x′x'x′ 后,算法接受 x′x'x′ 作为新解的概率)。
假设1:对称邻域结构
邻域结构是对称 的,即从 xxx 生成 x′x'x′ 的概率等于从 x′x'x′ 生成 xxx 的概率:Q(x→x′)=Q(x′→x) Q(x \to x') = Q(x' \to x) Q(x→x′)=Q(x′→x)
例如,TSP问题中"交换两个城市位置"的邻域结构是对称的:从解 xxx 交换位置 i,ji,ji,j 得到 x′x'x′,则从 x′x'x′ 交换位置 i,ji,ji,j 可回到 xxx,因此 Q(x→x′)=Q(x′→x)=1/∣N(x)∣Q(x \to x') = Q(x' \to x) = 1/|N(x)|Q(x→x′)=Q(x′→x)=1/∣N(x)∣(∣N(x)∣|N(x)|∣N(x)∣ 为邻域解的数量)。
假设2:接受更优解的概率为1
对于更优解(f(x′)≤f(x)f(x') \leq f(x)f(x′)≤f(x)),算法完全接受 (即 A(x→x′)=1A(x \to x') = 1A(x→x′)=1)。这是贪心算法的核心规则,也是模拟退火对贪心的"继承"。
推导接受概率 A(x→x′)A(x \to x')A(x→x′)
将假设1和假设2代入细致平衡条件,分两种情况讨论:
情况1:更优解(Δf=f(x′)−f(x)≤0\Delta f = f(x') - f(x) \leq 0Δf=f(x′)−f(x)≤0)
此时 A(x→x′)=1A(x \to x') = 1A(x→x′)=1,细致平衡条件简化为:PT(x)⋅1=PT(x′)⋅A(x′→x) P_T(x) \cdot 1 = P_T(x') \cdot A(x' \to x) PT(x)⋅1=PT(x′)⋅A(x′→x)
将玻尔兹曼分布 PT(x)=1Z(T)exp(−f(x)/(kT))P_T(x) = \frac{1}{Z(T)} \exp(-f(x)/(kT))PT(x)=Z(T)1exp(−f(x)/(kT)) 代入上式:1Z(T)exp(−f(x)kT)=1Z(T)exp(−f(x′)kT)⋅A(x′→x) \frac{1}{Z(T)} \exp\left(-\frac{f(x)}{kT}\right) = \frac{1}{Z(T)} \exp\left(-\frac{f(x')}{kT}\right) \cdot A(x' \to x) Z(T)1exp(−kTf(x))=Z(T)1exp(−kTf(x′))⋅A(x′→x)
两边约去 1/Z(T)1/Z(T)1/Z(T),整理得:A(x′→x)=exp(−f(x)−f(x′)kT)=exp(ΔfkT) A(x' \to x) = \exp\left(-\frac{f(x) - f(x')}{kT}\right) = \exp\left(\frac{\Delta f}{kT}\right) A(x′→x)=exp(−kTf(x)−f(x′))=exp(kTΔf)
由于 Δf≤0\Delta f \leq 0Δf≤0,exp(Δf/(kT))≤1\exp(\Delta f/(kT)) \leq 1exp(Δf/(kT))≤1,符合接受概率的取值范围(0≤A≤10 \leq A \leq 10≤A≤1)。
情况2:劣解(Δf=f(x′)−f(x)>0\Delta f = f(x') - f(x) > 0Δf=f(x′)−f(x)>0)
此时 x′x'x′ 是 xxx 的劣解,但 xxx 是 x′x'x′ 的更优解(因为 f(x)<f(x′)f(x) < f(x')f(x)<f(x′))。根据情况1的结论,从 x′x'x′ 到 xxx 的接受概率为 A(x′→x)=1A(x' \to x) = 1A(x′→x)=1。
将 A(x′→x)=1A(x' \to x) = 1A(x′→x)=1 代入细致平衡条件:PT(x)⋅A(x→x′)=PT(x′)⋅1 P_T(x) \cdot A(x \to x') = P_T(x') \cdot 1 PT(x)⋅A(x→x′)=PT(x′)⋅1
再代入玻尔兹曼分布:1Z(T)exp(−f(x)kT)⋅A(x→x′)=1Z(T)exp(−f(x′)kT) \frac{1}{Z(T)} \exp\left(-\frac{f(x)}{kT}\right) \cdot A(x \to x') = \frac{1}{Z(T)} \exp\left(-\frac{f(x')}{kT}\right) Z(T)1exp(−kTf(x))⋅A(x→x′)=Z(T)1exp(−kTf(x′))
两边约去 1/Z(T)1/Z(T)1/Z(T),整理得:A(x→x′)=exp(−f(x′)−f(x)kT)=exp(−ΔfkT) A(x \to x') = \exp\left(-\frac{f(x') - f(x)}{kT}\right) = \exp\left(-\frac{\Delta f}{kT}\right) A(x→x′)=exp(−kTf(x′)−f(x))=exp(−kTΔf)
由于 Δf>0\Delta f > 0Δf>0,exp(−Δf/(kT))≤1\exp(-\Delta f/(kT)) \leq 1exp(−Δf/(kT))≤1,符合接受概率的要求。
结论:Metropolis准则的完整形式
综合两种情况,Metropolis准则的接受概率为:A(x→x′)={1,Δf≤0(接受更优解)exp(−ΔfkT),Δf>0(概率接受劣解) A(x \to x') = \begin{cases} 1, & \Delta f \leq 0 \quad (\text{接受更优解}) \\ \exp\left(-\frac{\Delta f}{kT}\right), & \Delta f > 0 \quad (\text{概率接受劣解}) \end{cases} A(x→x′)={1,exp(−kTΔf),Δf≤0(接受更优解)Δf>0(概率接受劣解)
该准则保证了算法在温度 TTT 下的热力学平衡 ,即算法在温度 TTT 下迭代足够多次后,解的分布会收敛到玻尔兹曼分布。
3.3 降温策略的数学分析
降温策略是模拟退火的核心参数之一,需满足Geman兄弟收敛条件(1984年提出):
- limt→∞T(t)=0\lim_{t \to \infty} T(t) = 0limt→∞T(t)=0(温度最终趋近于0);
- ∑t=0∞T(t)=∞\sum_{t=0}^{\infty} T(t) = \infty∑t=0∞T(t)=∞(温度下降足够慢)。
只有满足这两个条件,算法才能以概率1收敛到全局最优解。以下是常见降温策略的数学形式与分析:
(1)指数降温(最常用)
数学形式:T(t+1)=α⋅T(t) T(t+1) = \alpha \cdot T(t) T(t+1)=α⋅T(t)
其中 α∈(0.8,0.99)\alpha \in (0.8, 0.99)α∈(0.8,0.99) 为降温速率(需接近1以保证缓慢冷却)。
分析:
- 初始温度 T0T_0T0 需足够高(如 T0=1000T_0 = 1000T0=1000),使初始接受概率约为0.8~0.9(即大部分劣解都能被接受);
- 降温速率 α\alphaα 越小,温度下降越快,但可能导致"淬火"(陷入局部最优);α\alphaα 越大,温度下降越慢,计算时间越长,但收敛性越好;
- 不满足Geman兄弟的第二个条件(∑t=0∞T(t)=T0/(1−α)<∞\sum_{t=0}^{\infty} T(t) = T_0/(1-\alpha) < \infty∑t=0∞T(t)=T0/(1−α)<∞),因此是近似收敛策略,但实际中性能较好。
(2)线性降温
数学形式:T(t+1)=T(t)−β T(t+1) = T(t) - \beta T(t+1)=T(t)−β
其中 β>0\beta > 0β>0 为降温步长 (需保证 T(t)>0T(t) > 0T(t)>0 直到终止)。
分析:
- 初始温度 T0T_0T0 和步长 β\betaβ 需匹配(如 T0=1000T_0 = 1000T0=1000,β=10\beta = 10β=10,则迭代100次后温度降至0);
- 同样不满足Geman兄弟的第二个条件(∑t=0∞T(t)\sum_{t=0}^{\infty} T(t)∑t=0∞T(t) 是有限的等差数列和),收敛性比指数降温差。
(3)对数降温(理论最优)
数学形式:T(t)=Cln(t+2) T(t) = \frac{C}{\ln(t+2)} T(t)=ln(t+2)C
其中 C>0C > 0C>0 为常数。
分析:
- 满足Geman兄弟的两个条件:limt→∞T(t)=0\lim_{t \to \infty} T(t) = 0limt→∞T(t)=0,且 ∑t=0∞T(t)=∞\sum_{t=0}^{\infty} T(t) = \infty∑t=0∞T(t)=∞(因为 ∑t=1∞1/lnt\sum_{t=1}^{\infty} 1/\ln t∑t=1∞1/lnt 发散);
- 降温极慢(如 t=1000t=1000t=1000 时,T(t)=C/ln(1002)≈C/6.9T(t) = C/\ln(1002) \approx C/6.9T(t)=C/ln(1002)≈C/6.9),实际中几乎无法使用(计算时间过长)。
(4)自适应降温(改进策略)
数学形式(基于接受率调整):T(t+1)={T(t)⋅(1+γ⋅(1−r)/r),r<rtargetT(t)⋅(1−γ),r>rtargetT(t),r=rtarget T(t+1) = \begin{cases} T(t) \cdot (1 + \gamma \cdot (1 - r)/r), & r < r_{\text{target}} \\ T(t) \cdot (1 - \gamma), & r > r_{\text{target}} \\ T(t), & r = r_{\text{target}} \end{cases} T(t+1)=⎩ ⎨ ⎧T(t)⋅(1+γ⋅(1−r)/r),T(t)⋅(1−γ),T(t),r<rtargetr>rtargetr=rtarget
其中:
- rrr 为当前温度下的接受率(接受的解数/总生成解数);
- rtargetr_{\text{target}}rtarget 为目标接受率(如0.5);
- γ∈(0.01,0.1)\gamma \in (0.01, 0.1)γ∈(0.01,0.1) 为调整步长。
分析:
- 根据接受率动态调整温度:若接受率过低(说明温度太低,探索不足),则提高温度;若接受率过高(说明温度太高,利用不足),则降低温度;
- 无需手动调整降温速率,适应性更强,但实现较复杂。
4. 模型求解完整步骤(精细化)
模拟退火的求解流程严格遵循"加热→缓慢冷却→结晶"的物理逻辑,以下是精细化的步骤分解(以最小化问题为例):
4.1 问题定义与预处理
首先明确优化问题的核心要素:
- 解空间 SSS:所有可能解的集合(如TSP中所有城市排列的集合);
- 目标函数 f(x)f(x)f(x):需最小化的函数(如TSP中的路径总长度);
- 邻域结构 N(x)N(x)N(x) :从解 xxx 经一次"微小扰动"可到达的所有邻接解的集合(需保证解空间连通,即任意两解可通过有限次邻域操作互达)。
预处理:
- 若解空间是连续的(如函数优化),需将其离散化(或用连续邻域,如高斯扰动);
- 若目标函数是最大化问题,需转换为最小化问题(如令 f′(x)=−f(x)f'(x) = -f(x)f′(x)=−f(x))。
4.2 参数初始化
设定算法的核心参数(需通过实验调整):
-
初始温度 T0T_0T0:
- 估计方法:随机生成 mmm 个邻域解对 (x,x′)(x, x')(x,x′),计算平均能量差 Δfavg=1m∑i=1m∣f(x′∗i)−f(xi)∣\Delta f_{\text{avg}} = \frac{1}{m} \sum_{i=1}^m |f(x'*i) - f(x_i)|Δfavg=m1∑i=1m∣f(x′∗i)−f(xi)∣,然后令 T0=−Δf∗avg/ln(P0)T_0 = -\Delta f*{\text{avg}} / \ln(P_0)T0=−Δf∗avg/ln(P0),其中 P0P_0P0 为初始接受概率(如 P0=0.8P_0 = 0.8P0=0.8);
- 示例:若 Δfavg=100\Delta f_{\text{avg}} = 100Δfavg=100,P0=0.8P_0 = 0.8P0=0.8,则 T0=−100/ln(0.8)≈470T_0 = -100 / \ln(0.8) \approx 470T0=−100/ln(0.8)≈470。
-
终止温度 TendT_{\text{end}}Tend:
- 需足够低(如 Tend=1e−6T_{\text{end}} = 1e-6Tend=1e−6),使接受概率几乎为0(即不再接受劣解);
- 或设定迭代次数上限(如1000次迭代)。
-
降温策略与参数:
- 若用指数降温,设定 α∈(0.9,0.99)\alpha \in (0.9, 0.99)α∈(0.9,0.99);
- 若用自适应降温,设定目标接受率 rtarget=0.5r_{\text{target}} = 0.5rtarget=0.5,调整步长 γ=0.05\gamma = 0.05γ=0.05。
-
Markov链长度 LLL:
- 每个温度下的迭代次数(需足够大,使算法在该温度下达到热平衡);
- 常见取值:L=100∼1000L = 100 \sim 1000L=100∼1000(或与解空间大小成正比,如TSP中 L=nL = nL=n,nnn 为城市数)。
-
初始解与最优解:
- 初始解 xcurrentx_{\text{current}}xcurrent:随机生成(或用启发式方法生成,如TSP的最近邻算法);
- 初始最优解 xbest=xcurrentx_{\text{best}} = x_{\text{current}}xbest=xcurrent,初始最优目标函数值 fbest=f(xcurrent)f_{\text{best}} = f(x_{\text{current}})fbest=f(xcurrent)。
4.3 迭代降温过程(精细化步骤)
重复以下步骤直到温度降至 TendT_{\text{end}}Tend 或满足终止条件:
步骤1:固定温度下的Markov链迭代(热平衡过程)
对于当前温度 TTT,进行 LLL 次邻域探索,记录接受率 rrr(用于自适应降温):
-
生成邻域解:
- 从邻域结构 N(xcurrent)N(x_{\text{current}})N(xcurrent) 中随机均匀 生成新解 x′x'x′(如TSP中随机选择两个位置交换城市);
- 若邻域结构是连续的(如函数优化),则用高斯扰动生成 x′=xcurrent+N(0,T)x' = x_{\text{current}} + \mathcal{N}(0, T)x′=xcurrent+N(0,T)(扰动强度与温度成正比)。
-
计算能量差:
- 计算 Δf=f(x′)−f(xcurrent)\Delta f = f(x') - f(x_{\text{current}})Δf=f(x′)−f(xcurrent)(注意符号:Δf<0\Delta f < 0Δf<0 表示 x′x'x′ 是更优解)。
-
Metropolis判断:
- 生成随机数 rrand∼Uniform(0,1)r_{\text{rand}} \sim \text{Uniform}(0, 1)rrand∼Uniform(0,1)(均匀分布在(0,1)之间);
- 若 Δf≤0\Delta f \leq 0Δf≤0(更优解):直接接受 x′x'x′,即 xcurrent=x′x_{\text{current}} = x'xcurrent=x′,fcurrent=f(x′)f_{\text{current}} = f(x')fcurrent=f(x′);
- 若 Δf>0\Delta f > 0Δf>0(劣解):计算接受概率 A=exp(−Δf/(kT))A = \exp(-\Delta f/(kT))A=exp(−Δf/(kT))(算法中 k=1k=1k=1),若 rrand<Ar_{\text{rand}} < Arrand<A,则接受 x′x'x′,否则保留原解;
- 记录接受次数(若接受 x′x'x′,则接受次数加1)。
-
更新最优解:
- 若 f(xcurrent)<fbestf(x_{\text{current}}) < f_{\text{best}}f(xcurrent)<fbest,则更新 xbest=xcurrentx_{\text{best}} = x_{\text{current}}xbest=xcurrent,fbest=f(xcurrent)f_{\text{best}} = f(x_{\text{current}})fbest=f(xcurrent);
- 注意:最优解是"全局最优",需单独保存,不随当前解的回退而改变。
-
计算接受率:
- 接受率 r=接受次数/Lr = \text{接受次数} / Lr=接受次数/L(用于自适应降温)。
步骤2:降温(根据策略调整温度)
根据预设的降温策略降低温度:
- 若用指数降温 :T=α⋅TT = \alpha \cdot TT=α⋅T;
- 若用线性降温 :T=T−βT = T - \betaT=T−β;
- 若用自适应降温 :根据接受率 rrr 调整温度(如4.3.3节的公式);
- 若用对数降温 :T=C/ln(t+2)T = C / \ln(t+2)T=C/ln(t+2)(ttt 为当前迭代次数)。
4.4 终止与输出
当满足以下任一条件时,终止算法:
- 温度降至终止温度 T≤TendT \leq T_{\text{end}}T≤Tend;
- 连续 KKK 个温度下最优解无改进(如 K=50K=50K=50);
- 迭代次数达到上限 t≥tmaxt \geq t_{\text{max}}t≥tmax。
输出结果:
- 全局最优解 xbestx_{\text{best}}xbest;
- 全局最优目标函数值 fbestf_{\text{best}}fbest;
- 迭代过程中的温度变化、接受率变化、最优解变化(可选)。
4.5 示例:TSP问题的模拟退火求解(精细化)
以旅行商问题(TSP)为例,具体说明每个步骤的实现细节:
-
问题定义:
- 输入:n个城市的坐标 (xi,yi)(x_i, y_i)(xi,yi)(i=1,2,...,ni=1,2,...,ni=1,2,...,n);
- 目标:寻找访问所有城市且仅访问一次的最短闭合路径。
-
解空间与邻域结构:
- 解空间 SSS:所有长度为n的城市排列的集合(如 x=[c1,c2,...,cn]x = [c_1, c_2, ..., c_n]x=[c1,c2,...,cn],表示访问顺序为 c1→c2→...→cn→c1c_1 \to c_2 \to ... \to c_n \to c_1c1→c2→...→cn→c1);
- 邻域结构:2-opt交换 (随机选择两个位置 i<ji < ji<j,反转 iii 到 jjj 之间的城市顺序),即 x′=[c1,...,ci−1,cj,cj−1,...,ci,cj+1,...,cn]x' = [c_1, ..., c_{i-1}, c_j, c_{j-1}, ..., c_i, c_{j+1}, ..., c_n]x′=[c1,...,ci−1,cj,cj−1,...,ci,cj+1,...,cn];
- 邻域大小:∣N(x)∣=n(n−1)/2|N(x)| = n(n-1)/2∣N(x)∣=n(n−1)/2(所有可能的2-opt交换)。
-
目标函数:
- 路径总长度 f(x)=∑k=1n−1d(ck,ck+1)+d(cn,c1)f(x) = \sum_{k=1}^{n-1} d(c_k, c_{k+1}) + d(c_n, c_1)f(x)=∑k=1n−1d(ck,ck+1)+d(cn,c1),其中 d(ck,cl)=(xk−xl)2+(yk−yl)2d(c_k, c_{l}) = \sqrt{(x_k - x_l)^2 + (y_k - y_l)^2}d(ck,cl)=(xk−xl)2+(yk−yl)2 (欧氏距离)。
-
参数初始化:
- 初始温度 T0=1000T_0 = 1000T0=1000;
- 终止温度 Tend=1e−6T_{\text{end}} = 1e-6Tend=1e−6;
- 降温速率 α=0.95\alpha = 0.95α=0.95;
- Markov链长度 L=2nL = 2nL=2n(如n=50,则L=100);
- 初始解:随机生成城市排列 xcurrentx_{\text{current}}xcurrent;
- 初始最优解 xbest=xcurrentx_{\text{best}} = x_{\text{current}}xbest=xcurrent,fbest=f(xcurrent)f_{\text{best}} = f(x_{\text{current}})fbest=f(xcurrent)。
-
迭代过程:
- 每个温度下进行 L=100L=100L=100 次2-opt交换,计算 Δf\Delta fΔf 并应用Metropolis准则;
- 每迭代10次记录一次温度、接受率和最优解;
- 当温度降至 1e−61e-61e−6 时终止,输出最短路径。
5. 适用边界与局限性(精细化)
5.1 适用问题类型
模拟退火是一种通用优化算法,尤其适合以下问题:
- 组合优化问题:如TSP、背包问题、作业调度问题(解空间离散、非凸,易陷入局部最优);
- 连续优化问题:如非线性函数最小值(多峰函数,梯度下降易陷入局部最优);
- 黑箱优化问题:目标函数无解析表达式(仅能通过数值计算得到,如工程设计中的性能仿真);
- 高维优化问题:解空间维度高(如100维以上),传统算法难以处理。
5.2 局限性(精细化分析)
-
参数敏感性:
- 初始温度 T0T_0T0 过低:算法初期探索不足,易陷入局部最优;
- 降温速率 α\alphaα 过小:温度下降过快,导致"淬火";
- Markov链长度 LLL 过短:算法在每个温度下未达到热平衡,解分布偏离玻尔兹曼分布;
- 参数调整需大量实验,无统一标准。
-
计算效率:
- 对于大规模问题(如n>1000的TSP),迭代次数多(每个温度下需 LLL 次邻域探索),计算时间长;
- 邻域结构复杂时(如n>1000的TSP,邻域大小为 O(n2)O(n^2)O(n2)),生成邻域解的时间增加。
-
理论收敛性与实际性能的矛盾:
- 理论最优的对数降温无法实际使用,实际中常用的指数降温是近似收敛;
- 即使使用指数降温,也可能因参数设置不当而收敛到局部最优。
-
邻域结构依赖性:
- 邻域结构需保证解空间连通,否则算法无法探索全空间(如TSP中仅用"交换相邻城市"的邻域结构不连通,需用2-opt交换);
- 邻域结构的大小影响计算效率(邻域越大,生成解的时间越长,但探索能力越强)。
5.3 改进方向(精细化)
-
并行模拟退火:
- 多进程同时搜索不同的解空间区域,定期交换最优解;
- 降低计算时间,提高探索能力(如基于MPI的并行实现)。
-
自适应模拟退火:
- 根据接受率动态调整温度、Markov链长度或邻域结构;
- 例如:若接受率低于0.1,增加Markov链长度 LLL;若接受率高于0.9,减小 LLL。
-
混合模拟退火:
- 结合其他算法的优势:
- 用遗传算法生成初始解(提高初始解质量);
- 用局部搜索(如2-opt、3-opt)优化模拟退火的当前解(提高利用能力);
- 用粒子群优化调整模拟退火的温度(提高适应性)。
- 结合其他算法的优势:
-
记忆化模拟退火:
- 记录迭代过程中访问过的解,避免重复探索;
- 例如:用哈希表存储已访问的解,若生成的邻域解已存在,则跳过。
-
多温度模拟退火:
- 同时维护多个温度的解,不同温度的解之间交换信息;
- 例如:高温解负责探索,低温解负责利用,定期将高温解的优解传递给低温解。
6. 总结(精细化)
模拟退火算法是物理启发式算法的典范,其核心贡献在于:
- 突破贪心算法的局限性:通过概率接受劣解,实现了从"局部最优"到"全局最优"的跨越;
- 平衡探索与利用:通过温度的缓慢下降,动态调整算法的探索(高温)和利用(低温)能力;
- 通用性强:无需依赖目标函数的梯度、凸性等性质,可处理各类优化问题。
理解模拟退火的关键是物理过程与算法的深度对应:
- 温度是"探索-利用"的调节旋钮;
- Metropolis准则是"概率接受劣解"的理论依据;
- 降温策略是"缓慢冷却"的实现方式;
- 玻尔兹曼分布是"热平衡"的数学描述。
尽管存在参数敏感、计算效率低等局限性,模拟退火仍是解决复杂优化问题的有效工具。在实际应用中,需根据问题特点调整参数和邻域结构,必要时结合其他算法进行改进,以达到最佳性能。
案例介绍
求解TSP问题:假设有10个城市,坐标为[(10,20), (30,40), (50,30), (70,20), (80,60), (60,70), (40,80), (20,70), (10,50), (30,10)],寻找访问所有城市且仅访问一次的最短闭合路径。
python
import numpy as np
import random
import matplotlib.pyplot as plt
def calculate_distance(city1, city2):
"""
计算两个城市间的欧氏距离
:param city1: 城市1坐标,元组形式 (x, y)
:param city2: 城市2坐标,元组形式 (x, y)
:return: 两个城市间的欧氏距离
"""
return np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
def calculate_total_distance(path, cities):
"""
计算一条路径的总长度
:param path: 城市访问顺序列表,如 [0, 1, 2, 3, 0]
:param cities: 所有城市的坐标列表,索引对应城市编号
:return: 路径总长度
"""
total = 0
# 遍历路径中的连续城市对,计算距离
for i in range(len(path) - 1):
total += calculate_distance(cities[path[i]], cities[path[i + 1]])
# 加上最后一个城市回到第一个城市的距离(闭合路径)
total += calculate_distance(cities[path[-1]], cities[path[0]])
return total
def generate_neighbor(path):
"""
生成路径的邻域解,采用2-opt交换策略
:param path: 原始路径列表
:return: 新的邻域路径列表
"""
# 随机选择两个不同的位置
i = random.randint(0, len(path) - 1)
j = random.randint(0, len(path) - 1)
# 确保i < j
if i > j:
i, j = j, i
# 反转i到j之间的城市顺序(2-opt交换)
new_path = path[:i] + path[i:j + 1][::-1] + path[j + 1:]
return new_path
def simulated_annealing_tsp(cities, initial_temperature=1000, final_temperature=1e-6, alpha=0.95, markov_length=20):
"""
模拟退火算法求解TSP问题
:param cities: 所有城市的坐标列表
:param initial_temperature: 初始温度
:param final_temperature: 终止温度
:param alpha: 降温速率(指数降温)
:param markov_length: 每个温度下的Markov链长度
:return: 最优路径和最优路径长度
"""
# 初始化当前路径:随机生成城市访问顺序(不重复)
current_path = list(range(len(cities)))
random.shuffle(current_path)
# 初始化最优路径为当前路径
best_path = current_path.copy()
# 计算当前路径和最优路径的长度
current_length = calculate_total_distance(current_path, cities)
best_length = current_length
# 当前温度
temperature = initial_temperature
# 记录迭代过程中的最优距离
history = []
history.append(best_length)
# 迭代降温过程
while temperature > final_temperature:
# 固定温度下的Markov链迭代
for _ in range(markov_length):
# 生成邻域解
neighbor_path = generate_neighbor(current_path)
# 计算邻域解的路径长度
neighbor_length = calculate_total_distance(neighbor_path, cities)
# 计算能量差(目标函数差)
delta_f = neighbor_length - current_length
# Metropolis准则判断是否接受邻域解
if delta_f <= 0:
# 更优解,直接接受
current_path = neighbor_path.copy()
current_length = neighbor_length
# 更新最优解
if current_length < best_length:
best_path = current_path.copy()
best_length = current_length
else:
# 劣解,计算接受概率
accept_prob = np.exp(-delta_f / temperature)
# 生成0-1间的随机数决定是否接受
if random.random() < accept_prob:
current_path = neighbor_path.copy()
current_length = neighbor_length
# 指数降温
temperature *= alpha
# 记录当前温度下的最优解
history.append(best_length)
# 返回最优路径、最优路径长度和迭代历史
return best_path, best_length, history
def main():
# 案例城市坐标数据
cities = [(10, 20), (30, 40), (50, 30), (70, 20), (80, 60), (60, 70), (40, 80), (20, 70), (10, 50), (30, 10)]
# 运行模拟退火算法
best_path, best_length, history = simulated_annealing_tsp(cities)
# 打印结果
print("最优路径:", best_path)
print("最短路径长度:", best_length)
# 可视化
plt.figure(figsize=(12, 5))
# 1. 绘制迭代收敛图
plt.subplot(1, 2, 1)
plt.plot(history, label='Best Distance')
plt.title('Iteration History')
plt.xlabel('Iterations (Temperature Steps)')
plt.ylabel('Total Distance')
plt.grid(True)
plt.legend()
# 2. 绘制最优路线图
plt.subplot(1, 2, 2)
# 获取路径上的坐标点
route_x = [cities[i][0] for i in best_path]
route_y = [cities[i][1] for i in best_path]
# 闭合路径:回到起点
route_x.append(route_x[0])
route_y.append(route_y[0])
plt.plot(route_x, route_y, 'o-', color='blue', label='Route')
# 标注城市编号
for i, (x, y) in enumerate(cities):
plt.text(x, y, f' {i}', fontsize=12, color='red')
plt.title('Best Route Map')
plt.xlabel('X Coordinate')
plt.ylabel('Y Coordinate')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()
一、前置数学背景(建模比赛必铺垫)
1.1 TSP问题的数学定义
给定 nnn 个城市的坐标集合 cities={(x1,y1),(x2,y2),...,(xn,yn)}\text{cities} = \{(x_1,y_1), (x_2,y_2), ..., (x_n,y_n)\}cities={(x1,y1),(x2,y2),...,(xn,yn)},寻找一个闭合回路 p=[p0,p1,...,pn−1]p = [p_0, p_1, ..., p_{n-1}]p=[p0,p1,...,pn−1](其中 pip_ipi 是城市索引,且每个索引仅出现一次),使得回路总长度最小:
L(p)=∑i=0n−2d(cities[pi],cities[pi+1])+d(cities[pn−1],cities[p0])L(p) = \sum_{i=0}^{n-2} d(\text{cities}[p_i], \text{cities}[p_{i+1}]) + d(\text{cities}[p_{n-1}], \text{cities}[p_0])L(p)=∑i=0n−2d(cities[pi],cities[pi+1])+d(cities[pn−1],cities[p0])
其中 d(⋅,⋅)d(\cdot,\cdot)d(⋅,⋅) 为欧氏距离。
1.2 模拟退火算法的数学逻辑
模拟退火(SA)是一种概率式启发式算法,模拟晶体退火的物理过程:
- 温度 TTT:控制搜索的"探索性"(高温=探索全局,低温=收敛局部)
- Metropolis准则 :接受新解的概率为
P(Δf)={1Δf≤0(新解更优)e−Δf/TΔf>0(以概率接受劣解)P(\Delta f) = \begin{cases} 1 & \Delta f \leq 0 \quad (\text{新解更优}) \\ e^{-\Delta f/T} & \Delta f > 0 \quad (\text{以概率接受劣解}) \end{cases}P(Δf)={1e−Δf/TΔf≤0(新解更优)Δf>0(以概率接受劣解)
其中 Δf=f新解−f当前解\Delta f = f_{\text{新解}} - f_{\text{当前解}}Δf=f新解−f当前解 是目标函数差。 - 温度调度 :指数降温 Tk+1=α⋅TkT_{k+1} = \alpha \cdot T_kTk+1=α⋅Tk(α∈(0.9,0.99)\alpha \in (0.9, 0.99)α∈(0.9,0.99) 为降温速率)
- Markov链长度:每个温度下执行的邻域搜索次数,确保当前温度下解空间充分采样。
二、代码模块逐行/逐块解析(建模比赛级细节)
模块1:依赖导入
python
import numpy as np
import random
numpy:提供高效的欧氏距离计算(虽本案例单对计算,仍为建模中处理坐标的习惯)random:生成随机初始解、邻域搜索的随机位置、劣解接受的随机概率。
模块2:辅助函数(距离计算类)
2.1 两城市欧氏距离计算
python
def calculate_distance(city1, city2):
return np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
数学对应 :直接实现欧氏距离公式 d=(x1−x2)2+(y1−y2)2d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}d=(x1−x2)2+(y1−y2)2
输入输出:
- 输入:
city1/city2为(x,y)元组的城市坐标 - 输出:浮点型距离值
细节 :用city1[0]/city1[1]索引坐标,符合Python元组的访问规则。
2.2 路径总距离计算
python
def calculate_total_distance(path, cities):
total = 0
for i in range(len(path) - 1):
total += calculate_distance(cities[path[i]], cities[path[i+1]])
total += calculate_distance(cities[path[-1]], cities[path[0]]) # 闭合回路
return total
数学对应 :实现TSP回路总长度公式 L(p)L(p)L(p)
输入输出:
- 输入:
path为城市索引的排列(如[0,1,2,...,9]),cities为坐标列表 - 输出:回路总长度的浮点值
关键细节: path[i]是当前访问的城市索引,cities[path[i]]是其实际坐标- 第7行:必须加上最后一个城市回到起点的距离,确保回路闭合。
模块3:邻域解生成(2-opt策略)
python
def generate_neighbor(path):
i = random.randint(0, len(path) - 1)
j = random.randint(0, len(path) - 1)
if i > j:
i, j = j, i # 确保i < j
new_path = path[:i] + path[i:j+1][::-1] + path[j+1:]
return new_path
2-opt的数学逻辑
2-opt是TSP邻域搜索的经典策略:通过反转路径中一段连续的子序列 ,消除路径交叉,优化总长度。
例如:原始路径 [A,B,C,D,E],若 i=1,j=3,则反转 [B,C,D] 为 [D,C,B],新路径为 [A,D,C,B,E]。
代码解析
- 第2-3行:随机生成两个城市索引位置 i,ji,ji,j
- 第4-5行:确保 i<ji < ji<j,否则反转后的子序列无效
- 第6行:切片拼接生成新路径:
path[:i]:保留 0∼i−10 \sim i-10∼i−1 的原始顺序path[i:j+1][::-1]:反转 i∼ji \sim ji∼j 的子序列([::-1]是Python列表反转语法)path[j+1:]:保留 j+1∼n−1j+1 \sim n-1j+1∼n−1 的原始顺序
细节 :2-opt操作能保证新路径仍为所有城市仅访问一次的有效TSP解。
模块4:核心模拟退火算法
python
def simulated_annealing_tsp(cities, initial_temperature=1000, final_temperature=1e-6, alpha=0.95, markov_length=20):
# 1. 初始化解
current_path = list(range(len(cities))) # 生成城市索引列表 [0,1,...,n-1]
random.shuffle(current_path) # 随机打乱作为初始解
best_path = current_path.copy() # 最优解初始化为当前解
current_length = calculate_total_distance(current_path, cities) # 当前解长度
best_length = current_length # 最优解长度
temperature = initial_temperature # 初始化温度
# 2. 降温迭代
while temperature > final_temperature:
# 3. 固定温度下的Markov链搜索
for _ in range(markov_length):
# 3.1 生成邻域解
neighbor_path = generate_neighbor(current_path)
neighbor_length = calculate_total_distance(neighbor_path, cities)
delta_f = neighbor_length - current_length # 目标函数差
# 3.2 Metropolis准则判断
if delta_f <= 0: # 新解更优,直接接受
current_path = neighbor_path.copy()
current_length = neighbor_length
# 更新全局最优解
if current_length < best_length:
best_path = current_path.copy()
best_length = current_length
else: # 新解为劣解,以概率接受
accept_prob = np.exp(-delta_f / temperature) # 接受概率
if random.random() < accept_prob: # 随机数判断
current_path = neighbor_path.copy()
current_length = neighbor_length
# 4. 指数降温
temperature *= alpha
return best_path, best_length
核心逻辑逐步对应
-
初始化阶段:
current_path:生成城市索引的随机排列(保证解的有效性)best_path = current_path.copy():必须用copy()复制列表内容,否则会因Python列表的引用传递导致最优解被意外修改。current_length/best_length:初始化目标函数值。
-
降温迭代:
- 循环终止条件:
temperature <= final_temperature(低温时仅接受最优解,收敛) initial_temperature=1000:足够大的初始温度,确保初始能接受大部分劣解(探索全局解空间)final_temperature=1e-6:足够小的终止温度,确保算法收敛到局部最优附近。
- 循环终止条件:
-
固定温度下的Markov链搜索:
markov_length=20:每个温度下执行20次邻域搜索,确保当前温度下解空间充分采样(避免过早收敛)delta_f = neighbor_length - current_length:目标函数差(因TSP求最小,Δf≤0\Delta f \leq 0Δf≤0 为优解)- Metropolis准则实现 :
- 优解直接接受,并更新当前解和全局最优解
- 劣解以
exp(-delta_f/temperature)概率接受(温度越高,接受概率越大,越易跳出局部最优)
-
降温策略:
temperature *= alpha:指数降温(最常用的调度策略,α=0.95\alpha=0.95α=0.95 是建模比赛的经验值)
-
返回结果:
- 返回整个搜索过程中全局最优解(而非终止时的当前解),确保结果最优。
模块5:主函数(问题实例调用)
python
def main():
# 10个城市的坐标数据
cities = [(10,20), (30,40), (50,30), (70,20), (80,60), (60,70), (40,80), (20,70), (10,50), (30,10)]
best_path, best_length = simulated_annealing_tsp(cities) # 调用SA算法
print("最优路径:", best_path)
print("最短路径长度:", best_length)
if __name__ == "__main__":
main()
解析:
cities为给定的10个城市坐标(索引0~9)if __name__ == "__main__"::确保仅在直接运行脚本时执行主函数(建模比赛中便于模块化调用)- 输出结果:每次运行可能不同(因随机初始化和邻域搜索),需多次运行取最优。
三、建模比赛注意事项与拓展
3.1 代码优缺点
- 优点:实现简单、效率高(适合100个城市以内的TSP)、能跳出局部最优
- 缺点 :结果有随机性、对大规模问题(n>200n>200n>200)效率较低
3.2 可改进方向
- 初始温度自适应 :根据解空间的目标函数差动态调整 T0T_0T0
- 邻域策略增强:结合2-opt和3-opt(消除更多路径交叉)
- Markov链长度自适应:随温度降低减少搜索次数
- 记忆机制:保存搜索过程中的最优解,避免丢失
- 结果稳定性:多次运行取最优解(可在主函数中加入循环运行逻辑)
四、代码运行示例结果(参考)
最优路径: [0, 9, 3, 2, 1, 8, 7, 6, 5, 4]
最短路径长度: 326.7871671970248
说明:路径索引对应城市坐标的顺序,实际路径需将索引转换为坐标序列后绘制。
通过以上解析,可直接用于建模比赛的代码说明、队友讲解或论文的算法实现部分。