2025全国大学生数学建模C题思路模型代码论文(开赛后第一时间更新),完整内容见文末名片,备战国赛,算法解析——模拟退火算法

模拟退火算法(Simulated Annealing, SA):从原理到实战步骤(小白友好 版)

一、算法原理:为什么叫“模拟退火”?

想象你是一块金属里的原子:高温时你活蹦乱跳(能量高、位置随机),随着温度降低,你慢慢“冷静”,最终和其他原子排成交错的“低能量队形”(比如金属结晶)。模拟退火算法就是模仿这个过程, 让“解”像原子一样,从“高温时的随机探索”到“低温时的精准优化”,最终找到全局最优解。

1.1 物理退火 vs 算法:用生活例子类比

物理退火过程 模拟退火算法对应 通俗理解
加热固体至高温 初始温度 T 很高 算法刚开始“精力充沛”,敢尝试各种 解
原子剧烈运动 (无序) 接受“差解”的概率高 即使新解比当前解差,也有很大可能尝 试
缓慢冷却 温度 T 逐渐降低 算法“精力减少”,越来越谨慎
原子趋于低能态 (有 序) 接受“差解”的概率趋近 于0 最终只接受更优解,锁定最优解区域

1.2 为什么需要模拟退火? 对比贪心算法

贪心算法:只接受“更优解”(比如走路只走下坡路),遇到局部最优(小山谷)就卡住,永远到不了全局最优(大山谷)。

模拟退火:允许“有条件接受差解”(高温时偶尔走上坡路),从而跳出小山谷,最终找到最低的大山谷。

二、数学模型:核心公式拆解 (带例子!)

假设我们要解决“最小化目标函数 f ( x ) f\left( x\right) f(x) ”(比如“最短旅行时间”“最低成本”),以下是核心模块的详细解释:

2.1 目标函数:解的 “好坏评分”

  • 定义: f ( x ) f\left( x\right) f(x) 是衡量解 x x x 优劣的函数,值越小解越好(最小化问题)。

例子:

旅行商问题 (TSP) : f ( x ) = f\left( x\right) = f(x)= 路径总长度 (城市顺序 x x x 的总距离);

函数优化: f ( x ) = x 2 f\left( x\right) = {x}^{2} f(x)=x2 (找 x x x 使 f ( x ) f\left( x\right) f(x) 最小,显然最优解是 x = 0 x = 0 x=0 )。

2.2 邻域生成:如何找“附近的解”?

从当前解 x x x 生成“邻居解” x ′ {x}^{\prime } x (记为 x ′ = N ( x ) {x}^{\prime } = N\left( x\right) x=N(x) ),就像“从当前位置走一小步”。邻域函数 N ( ⋅ ) N\left( \cdot \right) N() 需根据问题设计:

离散问题(如TSP):

例:当前解 x = [ 1 , 2 , 3 , 4 ] x = \lbrack 1,2,3,4\rbrack x=[1,2,3,4] (城市访问顺序),邻域解可通过“交换两个城市”生成: x ′ = [ 1 , 4 , 3 , 2 ] {x}^{\prime } = \lbrack 1,4,3,2\rbrack x=[1,4,3,2] (交换2和4)。

连续问题(如函数优化):

例:当前解 x = 3 x = 3 x=3 (优化 f ( x ) = x 2 f\left( x\right) = {x}^{2} f(x)=x2 ),邻域解可通过“加随机小扰动”生成: x ′ = 3 + 0.5 × rand ⁡ ( ) {x}^{\prime } = 3 + {0.5} \times \operatorname{rand}\left( \right) x=3+0.5×rand() (比如 x ′ = 3.2 {x}^{\prime } = {3.2} x=3.2 ,0.5 是扰动强度)。

2.3 接受概率:Metropolis准则(核心!)

生成邻域解 x ′ {x}^{\prime } x 后,要不要接受它?这由 Metropolis准则 决定,它是模拟退火“跳出局部最优”的关键!

分两种情况判断:

  1. x ′ {x}^{\prime } x 更优 ( f ( x ′ ) ≤ f ( x ) (f\left( {x}^{\prime }\right) \leq f\left( x\right) (f(x)f(x) ,对最小化问题):

2 必然接受 x ′ {x}^{\prime } x (就像下坡路,肯定要走)。

  1. x ′ {x}^{\prime } x 较差 ( f ( x ′ ) > f ( x ) ) \left( {f\left( {x}^{\prime }\right) > f\left( x\right) }\right) (f(x)>f(x)) :

1 以概率接受,公式为:

P ( 接受 x ′ ) = exp ⁡ ( Δ f T ) P\left( {\text{接受}{x}^{\prime }}\right) = \exp \left( {\frac{\Delta f}{T}}\right) P(接受x)=exp(TΔf)

其中:

  • Δ f = f ( x ′ ) f ( x ) > 0 {\Delta f} = f\left( {x}^{\prime }\right) f\left( x\right) > 0 Δf=f(x)f(x)>0 (解的“恶化程度”,差解比当前解差多少);

T T T :当前温度(温度越高,接受概率越大)。

例子: 直观理解接受概率

假设当前解 f ( x ) = 100 f\left( x\right) = {100} f(x)=100 ,邻域解 f ( x ′ ) = 105 ( Δ f = 5 ) f\left( {x}^{\prime }\right) = {105}\left( {{\Delta f} = 5}\right) f(x)=105(Δf=5) :

T = 100 T = {100} T=100 (高温): P = exp ⁡ ( 5 / 100 ) ≈ 0.95 P = \exp \left( {5/{100}}\right) \approx {0.95} P=exp(5/100)0.95 (95%概率接受,“大胆尝试”);

  • T = 10 T = {10} T=10 (中温): P = exp ⁡ ( 5 / 10 ) ≈ 0.61 P = \exp \left( {5/{10}}\right) \approx {0.61} P=exp(5/10)0.61 (61%概率接受,“谨慎尝试”);

  • T = 1 T = 1 T=1 (低温): P = exp ⁡ ( 5 / 1 ) ≈ 0.0067 P = \exp \left( {5/1}\right) \approx {0.0067} P=exp(5/1)0.0067 (0.67%概率接受,“几乎不尝试”)。

2.4 温度衰减:怎么“降温”?

温度 T T T 需从初始高温 T 0 {T}_{0} T0 慢慢降到终止温度 T end  {T}_{\text{end }} Tend  (比如 10 5 {10}^{5} 105 ),常用 指数衰减:

T k + 1 = α ⋅ T k    ( 0 < α < 1 ) {T}_{k + 1} = \alpha \cdot {T}_{k}\;\left( {0 < \alpha < 1}\right) Tk+1=αTk(0<α<1)

α \alpha α :衰减系数(比如 0.95,每次降温5%);

  • 例: T 0 = 1000 {T}_{0} = {1000} T0=1000 , α = 0.95 \alpha = {0.95} α=0.95 , 则 T 1 = 950 {T}_{1} = {950} T1=950 , T 2 = 902.5 {T}_{2} = {902.5} T2=902.5 , …, 逐渐变冷。

2.5 停止准则:什么时候结束?

  • 温度低到阈值: T ≤ T e n d T \leq {T}_{\mathrm{{end}}} TTend (比如 T e n d = 10 5 {T}_{\mathrm{{end}}} = {10}^{5} Tend=105 ,“冷透了”);

最优解不再改进:连续1000步最优解没变(“找不到更好的了”);

达到最大迭代次数:比如最多迭代10万次(“时间到了”)。

三、完整步骤:像“做饭流程”一样一步步来

以“TSP问题(找最短路径)”为例,带你走一遍模拟退火的完整流程:

步骤1: 准备食材 (参数初始化)

设置4个核心参数(根据问题调整):

  • 初始温度 T 0 {T}_{0} T0 :要足够高,让初始接受概率 P ≈ 0.8 ∼ 0.9 P \approx {0.8} \sim {0.9} P0.80.9 (“刚开始敢尝试”)。

,估计公式: T 0 = Δ f mit  ln ⁡ P 0 {T}_{0} = \frac{\Delta {f}_{\text{mit }}}{\ln {P}_{0}} T0=lnP0Δfmit  ( Δ f init  \Delta {f}_{\text{init }} Δfinit  是初始解周围差解的平均恶化量, P 0 = 0.85 {P}_{0} = {0.85} P0=0.85 是希望的初始接受概率)。

例:若初始解周围差解平均比它差 Δ f i n i t = 10 \Delta {f}_{\mathrm{{init}}} = {10} Δfinit=10 ,则 T 0 = 10 / ln ⁡ ( 0.85 ) ≈ 61 {T}_{0} = {10}/\ln \left( {0.85}\right) \approx {61} T0=10/ln(0.85)61 (取 T 0 = 60 {T}_{0} = {60} T0=60 )。

终止温度 T e n d {T}_{\mathrm{{end}}} Tend : 10 5 ∼ 10 3 {10}^{5} \sim {10}^{3} 105103 (比如 10 4 {10}^{4} 104 )。

衰减系数 α \alpha α : 0.85 ∼ 0.98 {0.85} \sim {0.98} 0.850.98 (比如 0.95,降温慢一点更精准)。

  • 内循环次数 L L L :每个温度下尝试 L L L 个邻域解(比如 L = 100 L = {100} L=100 ,“每个温度下试100步”)。

步骤2: 点火预热 (初始化解)

  • 随机生成初始解:比如TSP问题中,随机排列城市顺序 x 0 = [ 3 , 1 , 4 , 2 ] {x}_{0} = \lbrack 3,1,4,2\rbrack x0=[3,1,4,2] (城市1~4的访问顺序)。

计算初始目标函数: f ( x 0 ) = f\left( {x}_{0}\right) = f(x0)= 路径总长度(比如 f ( x 0 ) = 100 f\left( {x}_{0}\right) = {100} f(x0)=100 km)。

初始化变量:当前解 x = x 0 x = {x}_{0} x=x0 ,最优解 x best = x 0 {x}_{\text{best}} = {x}_{0} xbest=x0 ,最优值 f best = 100 {f}_{\text{best}} = {100} fbest=100

步骤3: 小火慢炖 (退火迭代)

T > T end  T > {T}_{\text{end }} T>Tend  时,重复以下内循环(每个温度下“炖 L L L 次”):

  1. 生成邻域解(试新解):

例:当前解 x = [ 3 , 1 , 4 , 2 ] x = \lbrack 3,1,4,2\rbrack x=[3,1,4,2] ,交换1和4,得到邻域解 x ′ = [ 3 , 4 , 1 , 2 ] {x}^{\prime } = \lbrack 3,4,1,2\rbrack x=[3,4,1,2]

  1. 计算目标函数: f ( x ′ ) = 95 f\left( {x}^{\prime }\right) = {95} f(x)=95 km(比当前解100 km更优)。

  2. 接受判断(Metropolis准则):

因为 f ( x ′ ) = 95 < f ( x ) = 100 f\left( {x}^{\prime }\right) = {95} < f\left( x\right) = {100} f(x)=95<f(x)=100 (更优),直接接受 x ′ {x}^{\prime } x ,当前解更新为 x = x ′ x = {x}^{\prime } x=x

f ( x ′ ) = 105 f\left( {x}^{\prime }\right) = {105} f(x)=105 (较差),当前 T = 60 T = {60} T=60 ,则 Δ f = 5 {\Delta f} = 5 Δf=5 ,接受概率 P = exp ⁡ ( 5 / 60 ) ≈ 0.92 P = \exp \left( {5/{60}}\right) \approx {0.92} P=exp(5/60)0.92 。生成随机数 r = 0.3 ( 0 ∼ 1  之间 ) r = {0.3}\left( {0 \sim 1\text{ 之间}}\right) r=0.3(01 之间) , r = 0.3 < 0.92 r = {0.3} < {0.92} r=0.3<0.92 ,接受 x ′ {x}^{\prime } x ,当前解更新为 x = x ′ x = {x}^{\prime } x=x

  1. 更新最优解:

若当前解 x x x f ( x ) = 95 < f best = 100 f\left( x\right) = {95} < {f}_{\text{best}} = {100} f(x)=95<fbest=100 ,则更新 x best = x {x}_{\text{best}} = x xbest=x , f best = 95 {f}_{\text{best}} = {95} fbest=95

  1. 降温:内循环 L = 100 L = {100} L=100 次后,降温 T = 0.95 × 60 = 57 T = {0.95} \times {60} = {57} T=0.95×60=57

步骤4: 关火出锅 (终止输出)

T ≤ T e n d T \leq {T}_{\mathrm{{end}}} TTend 时,输出最优解 x b e s t {x}_{\mathrm{{best}}} xbest (比如最短路径 [3,4,1,2])和最优值 f b e s t = 80 {f}_{\mathrm{{best}}} = {80} fbest=80 km。

四、关键参数:怎么调参不踩坑?

参数 作用 调参技巧 (小白版)
初始温度 ${T}_{0}$ 决定初始探索 范围 太高(如10000):算得慢;太低(如10):易陷局部最优,建议 试 ${T}_{0} = {50} \sim {200}$ 。
衰减系数 $\alpha$ 控制降温速度 接近1(如0.98):解更优但慢;接近0(如0.85):快但可能不精 准,建议 ${0.9} \sim {0.95}$ 。
内循环次 数 $L$ 每个温度下的 尝试次数 太小(如10):没试够就降温;太大(如10000):浪费时间,建 议 $L = {100} \sim {500}$ (问题规模越大, $L$ 越大)。

五、为什么模拟退火这么好用?

全局优化:概率接受差解,能跳出局部最优(“不会困在小山谷”);

普适性强:不管目标函数是否连续、可导,离散/连续问题都能用(“万能工具”);

实现简单:核心代码只需“邻域生成+接受概率+降温”,30行代码就能跑通。

总结:模拟退火= “有策略的探险”

模拟退火就像一位聪明的探险者:

出发时 (高温) : 精力旺盛,敢走崎岖山路 (接受差解) ,探索大范围地图;

途中(中温):体力下降,只敢走缓坡(低概率接受差解),缩小搜索范围;

终点(低温):精疲力尽,只走下坡路(只接受优解),最终找到最低山谷(全局最优)。

记住核心公式:接受差解的概率 P = exp ⁡ ( Δ f / T ) P = \exp \left( {{\Delta f}/T}\right) P=exp(Δf/T) ,温度越高、差解越“不差”(Δf越小),接受概率越大!

附:核心公式速查表

公式用途 公式(最小化问 题) 符号说明
接受概率(差 解) $P = \exp \left( {\frac{\Delta f}{T}}\right)$ ${\Delta f} = f\left( {x}^{\prime }\right) f\left( x\right) > 0$ , $T$ :当前温度
温度衰减 ${T}_{k + 1} = \alpha \cdot {T}_{k}$ $\alpha$ :衰减系数 $\left( {0 < \alpha < 1}\right)$
初始温度估计 ${T}_{0} = \frac{\Delta {f}_{\text{init }}}{\ln {P}_{0}}$ $\Delta {f}_{\text{init }}$ : 初始差解平均恶化量, ${P}_{0}$ : 初始接受概率(如 0.85)

Python实现代码:

模拟退火算法实现 (基于Rastrigin函数优化)

1. 导入必要库

import numpy as np # 用于数值计算:生成随机数(初始解/邻域扰动)、数组操作(解向量处理)、数学函数(指数/三角函数)

import matplotlib.pyplot as plt # 用于结果可视化:绘制收敛曲线(优化过程)、等高线图 (最优解位置)

2. 目标函数定义 (Rastrigin函数)

作用:作为优化目标,验证算法全局寻优能力。Rastrigin函数是典型多峰函数,存在大量局部极小值,全局最小值在原点 ( 0 , 0 , … , 0 ) \left( {0,0,\ldots ,0}\right) (0,0,,0) ,值为0。

公式: f ( x ) = A ⋅ n + ∑ i = 1 n ( x i 2 A ⋅ cos ⁡ ( 2 π x i ) ) f\left( x\right) = A \cdot n + \mathop{\sum }\limits_{{i = 1}}^{n}\left( {{x}_{i}^{2} A \cdot \cos \left( {{2\pi }{x}_{i}}\right) }\right) f(x)=An+i=1n(xi2Acos(2πxi)) ,其中 A = 10 A = {10} A=10 , n n n 为维度, x i ∈ [ 5.12 , 5.12 ] {x}_{i} \in \left\lbrack {{5.12},{5.12}}\right\rbrack xi[5.12,5.12]

def rastrigin(x):

"""

Rastrigin函数: 多峰测试函数, 用于评估解的优劣 (值越小越优)

参数:

x: 解向量 (numpy数组), 长度为维度n

返回:

函数值 (float): 值越小表示解越优

“”"

A = 10 # 函数固定参数,控制局部极小值数量

n = len(x) # 获取解向量维度 (问题维度)

#按公式计算函数值: A ∗ n + {A}^{ * }n + An+ 求和 ( x _ i 2 A ∗ cos ⁡ ( 2 π x _ i ) ) \left( {x\_ {i}^{2} {A}^{ * }\cos \left( {{2\pi x}\_ i}\right) }\right) (x_i2Acos(2πx_i)) ,每一项包含平方项和余弦波动项 (产生多峰)

return A * n + sum([(xi**2 A * np.cos(2 * np.pi * xi)) for xi in x])

3. 辅助函数 (生成初始解和邻域解)

作用:

generate_initial_solution: 在搜索空间内随机生成初始解,确保算法从随机点开始搜索。

generate_neighbor: 在当前解附近生成邻域解(添加高斯扰动),实现局部搜索。

def generate_initial_solution(dim, low, high):

"""

生成初始解: 在 [low, high] 范围内随机生成dim维解向量

参数:

dim: 解向量维度 (问题维度)

low: 每个维度的下界(搜索空间下限)

high: 每个维度的上界(搜索空间上限)

返回:

初始解向量(numpy数组):均匀分布随机数组成的dim维向量

“”"

#生成均匀分布随机数:low≤每个维度值<high, size=dim指定向量长度(维度)

return np.random.uniform(low=low, high=high, size=dim)

def generate_neighbor(current_solution, step_size):

"""

生成邻域解: 在当前解基础上添加高斯分布扰动 (局部搜索)

参数:

current_solution: 当前解向量(numpy数组),需生成邻域解的基准点

step_size: 扰动步长 (高斯分布标准差), 控制邻域搜索范围 (步长越小搜索越精细)

返回:

邻域解向量 (numpy数组): 当前解 + 高斯扰动

“”"

#生成高斯分布扰动:均值loc=0(扰动中心在当前解),标准差scale=step_size(扰动幅度),维度与当前解一致

			perturbation = np.random.normal(loc=0, scale=step_size,

size=current_solution.shape)

return current_solution + perturbation # 当前解叠加扰动,得到邻域解

4. 模拟退火主函数

作用:实现模拟退火核心逻辑:初始化解→温度衰减→邻域搜索→Metropolis接受准则→最优解更新,最终输出全局最优解。

def simulated_annealing(objective_func, dim, initial_temp, cooling_rate,

final_temp, solution_bounds, step_size):

"""

模拟退火算法主函数:通过模拟物理退火过程(高温探索→低温收敛)寻找全局最优解

参数:

objective_func: 目标函数(需最小化的函数,如rastrigin)

							dim:解向量维度(问题维度)

							initial_temp: 初始温度(控制探索能力,值越高接受差解概率越大)

							cooling_rate: 降温速率 $\left( { \in  \left( {0,1}\right) \text{,值越大降温越慢,探索更充分但迭代次数多}}\right)$

							final_temp: 终止温度 (温度低于此值时停止迭代, 算法收敛)

							solution_bounds: 解向量取值范围(列表,[low, high],每个维度的上下界)

							step_size: 邻域解扰动步长 (高斯分布标准差, 控制局部搜索精度)

			返回:

							best_solution: 全局最优解向量(numpy数组)

							best_value: 全局最优解对应的目标函数值(float)

							best_values_history: 每轮迭代的最优值记录(列表,用于绘制收敛曲线)

			"""

																												初始化

			low, high = solution_bounds # 解空间上下界 (从参数提取, 便于后续使用)

			current_solution = generate_initial_solution(dim, low, high) # 随机生成初始解

(起点)

			current_value = objective_func(current_solution) # 计算初始解的目标函数值 (初始

能量)

#初始化最优解: 初始时最优解为当前解 (尚无其他解可比较)

best_solution = current_solution.copy( ) # 深拷贝当前解(避免后续修改影响最优解)

best_value = current_value # 初始最优值 = 初始解的目标函数值

#记录每轮迭代的最优值: 用于后续绘制收敛曲线, 观察算法优化过程

best_values_history = [best_value] # 列表初始化,存入初始最优值

temp = initial_temp # 当前温度: 从初始温度开始退火

退火过程 (核心迭代)

while temp > final_temp: # 温度未降至终止温度时,持续迭代搜索

#1. 生成邻域解并限制在搜索空间内

neighbor_solution = generate_neighbor(current_solution, step_size) # 基于

当前解生成邻域解(加高斯扰动)

neighbor_solution = np.clip(neighbor_solution, low, high) # 截断超出范围的解: 若某维度值>high则设为high, <low则设为low

#2. 计算邻域解的目标函数值 (能量)

neighbor_value = objective_func(neighbor_solution)

#3. Metropolis接受准则: 决定是否接受邻域解(核心机制,控制探索与收敛)

delta = current_value neighbor_value # 能量差:当前解值 邻域解值(正→邻域解更优,负 → \rightarrow 邻域解更差)

if delta > 0: # 邻域解更优 (目标函数值更小), 直接接受

current_solution = neighbor_solution.copy( ) # 更新当前解为邻域解

current_value = neighbor_value # 更新当前解的目标函数值

else: # 邻域解更差 (目标函数值更大), 按概率接受 (避免陷入局部最优)

acceptance_prob = np.exp(delta / temp) # 接受概率: exp(ΔE/T), ΔE=delta (负),温度T越高概率越大

		if np.random.rand(   ) < acceptance_prob: # 生成[0,1)随机数, 小于接受概率

则接受差解

				current_solution = neighbor_solution.copy(   ) # 更新当前解为邻域解

				current_value = neighbor_value # 更新当前解的目标函数值

	#4. 更新全局最优解: 若当前解优于历史最优, 则更新最优解

	if current_value < best_value: # 当前解的目标函数值 < 历史最优值→找到了更优

		best_solution = current_solution.copy(   ) # 更新最优解向量

		best_value = current_value # 更新最优值

	#5. 记录当前轮次的最优值: 存入历史列表, 用于后续可视化

	best_values_history.append(best_value)

	#6. 温度衰减:按降温速率降低当前温度(退火核心,控制探索→收敛过渡)

	temp *= cooling_rate # 指数降温:T_new = T_old * cooling_rate (常用降温方

式)

#

return best_solution, best_value, best_values_history # 返回搜索结果

5. 主程序 (参数设置、运行算法、结果可视化)

作用:配置算法参数 → \rightarrow 调用模拟退火函数 → \rightarrow 输出最优解 → \rightarrow 可视化优化过程(收敛曲线+等高线图)。

if name == “main”: # 主程序入口: 当脚本直接运行时执行,被导入时不执行

参数设置 (需根据问题调整)

dim = 2 # 解向量维度: 设为 2 便于绘制二维等高线图 (可视化最优解位置)

initial_temp = 100.0 # 初始温度:控制初期探索能力,值越高越容易接受差解(避免局部最优)

cooling_rate = 0.95 # 降温速率: 建议0.9~0.99,值越大降温越慢(探索更充分但迭代次数多)

final_temp = 1e8 # 终止温度: 温度<<1时, 算法基本收敛 (接受差解概率 ≈0)

solution_bounds = [5.12,5.12] # 解空间范围:Rastrigin函数标准测试范围(每个维度均在此区间)

step_size = 0.1 # 邻域扰动步长: 高斯分布标准差, 控制局部搜索精度 (步长越小搜索越精细,建议 0.01 ∼ 0.5 {0.01} \sim {0.5} 0.010.5 )

					运行模拟退火算法

best_solution, best_value, best_values_history = simulated_annealing( )

objective_func=rastrigin, # 目标函数: 选择Rastrigin函数

dim=dim,

initial_temp=initial_temp,

cooling_rate=cooling_rate,

final_temp=final_temp,

solution_bounds=solution_bounds,

step_size=step_size

)

#

#输出最优结果

print("===== 模拟退火算法优化结果 =====")

print(f"最优解 (Best Solution): \{np.round(best_solution, 4)\}") # 保留4位小数,

展示最优解向量

print(f"最优值 (Best Value): \{np.round(best_value, 6)\}") # 保留6位小数, 展示最

优目标函数值(理想值为 0 )

#

#可视化结果(直观展示优化效果)

#1. 绘制最优值随迭代的收敛曲线: 观察算法是否逐步逼近最优解

plt.figure(figsize=(10,5)) # 创建图形窗口,宽10英寸、高5英寸

plt.plot(best_values_history, label='Best Value', color='blue', linewidth=1.5)

#绘制历史最优值曲线

plt.xlabel('Iteration', fontsize=12) # x轴标签: 迭代次数(每轮温度下降为1次迭

代)

plt.ylabel('Objective Function Value', fontsize=12) # y轴标签: 目标函数值 (越

小越优)

plt.title('Convergence Curve of Simulated Annealing', fontsize=14, pad=15) #

图表标题

plt.grid(True, linestyle='', alpha=0.7) # 添加网格线: 虚线、透明度0.7 (辅助观

察趋势)

plt.legend(fontsize=10) # 显示图例 (标注曲线名称)

plt.tight_layout(   ) # 自动调整布局 (避免标签被截断)

plt.show(   ) # 显示图形

#2. 若维度 $= 2$ ,绘制目标函数等高线+最优解位置:直观展示最优解在搜索空间中的位置

if dim == 2 :

	#生成网格数据: 覆盖整个搜索空间, 用于计算等高线

		low, high = solution_bounds # 提取解空间上下界(之前参数设置的

[ 5.12 , 5.12 ] ) \left\lbrack {{5.12},{5.12}}\right\rbrack ) [5.12,5.12])

	x = np.linspace(low, high, 100) # x轴网格点: 从low到high均匀取100个点 (分辨

率)

	y = np.linspace(low, high, 100) # y轴网格点: 同上

	$X, Y = {np}.{meshgrid}\left( {x, y}\right) \;\#$ 创建网格矩阵: $X/Y$ 为 ${100} \times  {100}$ 矩阵,对应平面所有点坐

	Z = np.zeros_like(X) # 初始化目标函数值矩阵: 与X同维度 (100x100)

	#计算网格上每个点的Rastrigin函数值(用于绘制等高线)

	for i in range(X.shape[0]): # 遍历x轴网格点 (共100行)

		for j in range(X.shape[1]): # 遍历y轴网格点 (共100列)

				Z[i, j] = rastrigin(np.array([X[i, j], Y[i, j]]]) # 计算点

( X [ i , j ] , Y [ i , j ] ) \left( {\mathrm{X}\left\lbrack {\mathrm{i},\mathrm{j}}\right\rbrack ,\mathrm{Y}\left\lbrack {\mathrm{i},\mathrm{j}}\right\rbrack }\right) (X[i,j],Y[i,j]) 的函数值

	#绘制等高线图

	plt.figure(figsize=(8,6)) # 创建图形窗口,宽8英寸、高6英寸

	contour = plt.contourf(X, Y, Z, 50, cmap='viridis') # 填充等高线: 50层颜色

渐变, viridis色系(蓝→黄→红,值递增)

	plt.colorbar(contour, label='Objective Function Value') # 添加颜色条: 标注

颜色对应的函数值

	#标记最优解位置: 红色五角星, 大小100

	plt.scatter(   )

		best_solution[0], # 最优解x坐标

		best_solution[1], # 最优解y坐标

		c='red', # 颜色:红色

		s=100, # 大小: 100 (像素)

		marker='*', # 形状: 五角星 (突出显示最优解)

		label='Best Solution' # 标签:用于图例

	)

	plt.xlabel('x1', fontsize=12) # x轴标签: 解向量第一维度

	plt.ylabel('x2', fontsize=12) # y轴标签: 解向量第二维度

		plt.title('Rastrigin Function Contour & Best Solution', fontsize=14,

pad=15) # 图表标题

	plt.legend(fontsize=10) # 显示图例 (标注最优解)

	plt.tight_layout(   ) # 自动调整布局

	plt.show(   ) # 显示图形

代码逐板块讲解

1. 库导入

numpy:提供数值计算基础工具,如np.random.uniform(生成初始解)、np.random.normal(生成邻域扰动)、np.clip(限制解空间)、np.exp(计算Metropolis接受概率)。

matplotlib.pyplot:用于可视化,通过plt.plot绘制收敛曲线(观察优化过程), plt.contourf绘制等高线图(展示最优解在搜索空间中的位置)。

2. 目标函数 (Rastrigin)

核心作用:作为优化目标,验证算法是否能在多峰环境中找到全局最优。

关键细节:函数包含平方项( x i 2 {x}_{i}^{2} xi2 )和余弦波动项( $ A\cos \left( {{2\pi }{x}_{i}}\right)$ ),后者导致函数在搜索空间内产生大量局部极小值(类似“波浪”),全局最小值仅在原点(0,0,…,0)处取得,值为0。

3. 辅助函数

generate_initial_solution:

生成随机初始解,确保算法从搜索空间中均匀采样的点开始,避免初始位置对结果的影响。

np.random.uniform(low, high, size=dim): 生成dim维均匀分布向量,每个维度值在[low, high)内。

generate_neighbor: 生成邻域解,实现局部搜索。通过高斯分布扰动(中心在当前解,幅度由step_size控制),确保搜索具有随机性和局部性。

np.random.normal(loc=0, scale=step_size, …): 生成均值0、标准差step_size的高斯扰动,叠加到当前解上得到邻域解。

4. 模拟退火主函数 (核心逻辑)

初始化:

生成初始解→计算初始值→初始化最优解(初始解即为最优解)→记录历史最优值。

退火迭代 (while temp > final_temp):

  1. 邻域搜索:生成邻域解并截断到搜索空间内(np.clip),避免解超出边界。

  2. 能量计算:计算邻域解的目标函数值(能量)。

  3. Metropolis准则:

若邻域解更优(delta = current_value neighbor_value > 0),直接接受。

若邻域解较差(delta < 0),按概率exp(delta/temp)接受(温度越高,接受概率越大, 允许“跳出”局部最优)。

  1. 最优解更新:若当前解优于历史最优,更新全局最优解和最优值。

  2. 历史记录: 将当前最优值存入列表,用于后续可视化。

  3. 温度衰减:temp *= cooling_rate(指数降温),控制退火过程从“探索”(高温)到“收敛”(低温)的过渡。

5. 主程序 (参数设置与结果展示)

参数设置:

dim=2: 维度设为2,便于可视化(二维等高线图)。

initial_temp=100: 初始温度需足够高 (如100) ,确保初期能接受差解,探索全局。

cooling_rate=0.95:指数降温速率,0.95表示每轮温度降低5%,平衡探索与迭代效率。

final_temp=1e8: 终止温度足够低(≈0),此时接受差解概率≈0,算法收敛。

solution_bounds=[5.12,5.12]: Rastrigin函数标准测试范围,确保解在有效区域内。

step_size=0.1:邻域扰动步长,控制局部搜索精度(步长太小易陷入局部最优,太大则搜索粗糙)。

结果可视化:

收敛曲线:展示best_values_history随迭代的变化,理想情况下曲线应逐步下降并趋于稳定 (接近0)。

等高线图:仅在dim=2时绘制,通过颜色深浅展示目标函数值分布(深色为低值区域),红色五角星标记最优解位置,直观验证是否接近原点(全局最优)。

关键参数调整建议

初始温度:若算法过早收敛(陷入局部最优),可增大initial_temp(如200);若迭代过慢,可减小(如50)。

降温速率:若搜索空间复杂(多峰密集),用较大cooling_rate(如0.99),延长探索时间;简单空间用较小值(如0.9)。

步长step_size:若最优值波动大(未收敛),减小步长(如0.05);若收敛到局部最优,增大步长 (如0.5)以扩大搜索范围。

运行结果说明

最优解:理想情况下应接近0.0, 0.0,实际受随机因素影响可能有微小偏差(如 [0.0012, 0.0008]) 。

最优值:应接近0(如<1e4),值越小表示越接近全局最优。

收敛曲线:应呈现“快速下降→缓慢收敛”趋势,最终趋于平稳(无明显波动)。

等高线图:最优解应位于颜色最深的区域(低函数值区域),且接近等高线中心(原点)。

通过以上实现,模拟退火算法能有效在多峰函数中搜索全局最优,代码逻辑清晰、参数可调,可根据具体优化问题进一步修改(如更换目标函数、调整维度或参数)。

Matlab实现代码:

模拟退火算法Matlab代码检查与优化说明

检查结论

原代码整体符合要求:严格遵循Matlab语法规范,变量名均为英文,逻辑清晰,无语法/逻辑错误,自定义函数与主程序分离,注释较详细。仅需补充部分行内注释以满足“每一行详细批注”要求,优化后代码如下。

1. 目标函数(自定义函数):sphere_function.m

function f = f = f= sphere_function(x)

% 目标函数:Sphere函数(用于模拟退火算法测试的经典凸函数)

% 输入: x n维列向量 (待优化的解向量)

% 输出: f f f 目标函数值 ( f ( x ) = x 1 2 + x 2 2 + … + x n 2 \left( {f\left( x\right) = {x}_{1}{}^{2} + {x}_{2}{}^{2} + \ldots + {x}_{n}{}^{2}}\right. (f(x)=x12+x22++xn2 ,理论最小值为 θ \theta θ ,在 x = ( 0 , 0 , … , 0 ) x = \left( {0,0,\ldots ,0}\right) x=(0,0,,0) 处取到)

f = sum(x .^ 2); % 计算向量各元素的平方和 (核心计算逻辑)

end

2. 邻域解生成函数(自定义函数):generate_neighbor.m

function x_new = generate_neighbor(x_current, x_min, x_max)

% 生成邻域解: 在当前解附近添加随机扰动, 确保新解在搜索范围内

% 输入:

% x_current 当前解(n维列向量,算法迭代过程中的当前状态)

% x_min 解的下界(标量,各维度通用的最小值约束)

% x_max 解的上界 (标量, 各维度通用的最大值约束)

% 输出:

% x_new 邻域解(n维列向量,满足x_min ≤ x_new(i) ≤ x_max)

n = length(x_current); % 获取解的维度 (n为问题维度)

step = 0.02 * (x_max x_min); % 扰动步长 (设为搜索范围的2%,平衡局部探索能力)

% 生成随机扰动:rand(n,1)生成 [ 0 , 1 ) \lbrack 0,1) [0,1) 均匀分布随机数,减去 0.5 后范围为 [ 0.5 , 0.5 ) \lbrack {0.5},{0.5}) [0.5,0.5) ,乘以step

得到[step/2, step/2]的扰动

x_new = x_current + step * (rand(n, 1) 0.5);

% 边界约束处理:确保新解不超出[x_min, x_max]范围

x_new(x_new < x_min) = x_min; % 低于下界的元素强制设为下界

x_new(x_new > x_max) = x_max; %高于上界的元素强制设为上界

end

3. 主程序:simulated_annealing.m

% 模拟退火算法主程序: 求解n维Sphere函数的全局最小值

% 核心思想: 模拟物理退火过程, 通过控制温度下降实现全局寻优, 避免陷入局部最优

参数设置(Problem & Algorithm Parameters)

% 问题参数 (Problemspecific Parameters)

n = 2; % 解的维度 (设置为 2 便于可视化二维空间的优化过程)

x_min = 5; % 解向量各维度的下界 (搜索空间左边界)

x_max = 5; % 解向量各维度的上界 (搜索空间右边界)

% 算法参数 (SAspecific Parameters)

T0 = 100; % 初始温度 (控制初始接受概率, 需足够高以跳出局部最优)

alpha = 0.95; % 降温系数 (0 < alpha < 1, 值越大降温越慢, 搜索更充分但耗时更

长)

L = 100; % 各温度下的迭代次数(马尔可夫链长度,控制当前温度的平衡时间)

Tf = 1e5; % 停止温度 (温度低于此值时终止迭代, 视为系统"冻结")

%

% 初始化(Initialization)

% 生成初始解:在[x_min, x_max]范围内随机均匀采样n维向量

x_current = x_min + (x_max x_min) * rand(n, 1); % rand(n,1)生成n×1的[0,1)随机数, 缩放至目标范围 % 计算初始解的目标函数值 (评估初始解质量) f_current = sphere_function(x_current); % 初始化最优解记录器 (跟踪迭代过程中的全局最优)

x_best = x_current; % 当前全局最优解(初始化为初始解)

f_best = f_current; % 当前全局最优函数值(初始化为初始解的函数值)

% 初始化收敛曲线数据(记录每轮温度迭代的最优值,用于后续可视化)

f_best_history = f_best; % 用初始最优值作为历史记录的第一个元素

%

模拟退火核心迭代(SA Iteration)

T = T0; % 当前温度初始化 (从初始温度T0开始降温)

% 主循环:温度高于停止温度时持续迭代 (模拟退火过程)

while T > Tf

% 温度 $\mathrm{T}$ 下的平衡过程:迭代 $\mathrm{L}$ 次以达到热平衡(马尔可夫链采样)

for i = 1 : L

	% 步骤1: 生成邻域解(当前解附近的扰动解)

	x_new = generate_neighbor(x_current, x_min, x_max);

	% 步骤2: 计算新解的目标函数值 (评估新解质量)

	f_new = sphere_function(x_new);

	% 步骤3: 计算能量差(目标函数值的变化量, delta $< \theta$ 表示新解更优)

	delta = f_new  f_current;

	% 步骤4: Metropolis准则 (决定是否接受新解, 核心机制)

	if delta < 0 % 新解更优 (目标函数值更小): 无条件接受

		x_current = x_new; % 更新当前解为新解

		f_current = f_new; % 更新当前解的函数值

		% 检查是否更新全局最优解(若新解是历史最佳, 则记录)

		if f_new < f_best

			x_best = x_new; % 更新全局最优解向量

			f_best = f_new; % 更新全局最优函数值

		end

	else % 新解较差 (目标函数值更大): 以概率 $\exp \left( {\text{delta}/\mathrm{T}}\right)$ 接受 (模拟热运动的随机

性)

		r = rand; % 生成[0,1)的随机数 (用于与接受概率比较)

		% 接受概率P = exp(delta/T): 温度越高/能量差越小,接受概率越大

		if r < exp(delta / T)

			x_current = x_new; % 以概率接受新解, 更新当前解

			f_current = f_new; % 更新当前解的函数值

		end

	end

end % 结束当前温度的L次迭代 (平衡过程)

% 记录当前温度下的最优值 (用于绘制收敛曲线)

f_best_history = [f_best_history; f_best]; % 在历史数组末尾添加新的最优值

% 降温操作:按系数alpha降低当前温度(几何降温策略)

T = alpha * T; % 打印当前状态 (监控迭代进度: 温度和最优值) fprintf(‘Temperature: %.6f, Best f: %.6f\n’, T, f_best) end % 结束模拟退火主循环(温度降至Tf以下) %

% 结果输出(Result Output)

% 打印优化结果(控制台输出关键信息)

fprintf('\n Optimization Results

fprintf(‘Best solution x: [%.6f, %.6f]\n’, x_best(1), x_best(2)); % 输出二维最优解

(n=2时)

fprintf(‘Minimum function value f: %.6f\n’, f_best) % 输出最优函数值

=\n’);

%

% 可视化(Visualization)

% 图1: 目标函数等高线与优化结果对比 (直观展示解的位置)

figure(1); % 创建第一个图形窗口

% 生成网格数据:在[x_min, x_max]范围内生成100×100的网格点

[x1, x2] = meshgrid(linspace(x_min, x_max, 100), linspace(x_min, x_max, 100));

[ m , ∼ ] = size ⁡ ( x 1 ) ;    % \left\lbrack {m, \sim }\right\rbrack = \operatorname{size}\left( {x1}\right) ;\;\% [m,]=size(x1);% 获取网格尺寸 ( m = 100 ,网格为 100 × 100 ) \left( {m = {100}\text{,网格为}{100} \times {100}}\right) (m=100,网格为100×100)

f_mesh = zeros(m, m); %初始化目标函数值网格 (存储每个网格点的函数值)

% 计算网格点的目标函数值(填充f_mesh)

for i = 1 : m i = 1 : m i=1:m % 遍历网格行索引

for $j = 1 : m$ % 遍历网格列索引

	x = [x1(i, j); x2(i, j)]; % 构造二维点(x1(i, j), x2(i, j))的列向量

	f_mesh(i, j) = sphere_function(x); % 计算该点的Sphere函数值

end

end

% 绘制等高线图 (可视化目标函数地形)

contour(x1, x2, f_mesh, 30); % 绘制30条等高线, 线条越多地形细节越丰富

hold on; % 保持当前图形, 后续绘图叠加在此图上

% 绘制理论最小值点(Sphere函数最小值在原点(0,0))

plot(0,0,‘r*’, ‘MarkerSize’, 12, ‘LineWidth’, 2); % 红色星号标记理论最优

% 绘制SA优化结果 (算法找到的最优解)

plot(x_best(1), x_best(2), ‘bo’, ‘MarkerSize’, 10, ‘LineWidth’, 2); % 蓝色圆点标记

SA结果

% 图形标注 (增强可读性)

xlabel(‘x_1’); % x轴标签 (解向量第一维度)

ylabel(‘x_2’); % y轴标签 (解向量第二维度)

title(‘Sphere Function Contour & SA Result’); % 图形标题

legend(‘Contour Lines’, ‘Theoretical Minimum (0,0)’, ‘SA Optimized Result’); % 图

grid on; % 显示网格线 (辅助定位)

% 图2: 最优值收敛曲线 (展示算法收敛过程)

figure(2); % 创建第二个图形窗口

plot(f_best_history); % 绘制每轮温度迭代的最优函数值

% 图形标注

xlabel(‘Temperature Iteration Steps’); % x轴: 温度迭代次数(每步对应一个温度T)

ylabel(‘Best Function Value’); % y轴: 该温度下的全局最优值

title(‘Convergence Curve of Simulated Annealing’); % 图形标题

grid on; % 显示网格线 (辅助观察数值变化)

%

代码逐板块详解

1. 自定义函数说明

sphere_function.m

功能:定义优化目标函数Sphere函数 f ( x ) = ∑ i = 1 n x i 2 f\left( \mathbf{x}\right) = \mathop{\sum }\limits_{{i = 1}}^{n}{x}_{i}^{2} f(x)=i=1nxi2

特点:凸函数,全局最小值唯一 ( x = ( 0 , 0 , … , 0 ) , f = 0 ) \left( {\mathbf{x} = \left( {0,0,\ldots ,0}\right) \text{,}f = 0}\right) (x=(0,0,,0)f=0) ,适合验证算法寻优能力。

参数:输入x为n维列向量,输出f为标量函数值。

generate_neighbor.m

功能:生成当前解的邻域解(局部搜索),确保新解在搜索空间内。

核心逻辑:

  1. 扰动步长:设为搜索范围的2% (step = 0.02*(x_maxx_min)) ,平衡局部探索精度与全局搜索范围。

  2. 随机扰动:在当前解 x current {\mathbf{x}}_{\text{current}} xcurrent 基础上添加[ s t e p / 2 , s t e p / 2 {step}/2,{step}/2 step/2,step/2 ]的均匀随机扰动。

  3. 边界约束:通过逻辑索引强制新解 x new {\mathbf{x}}_{\text{new}} xnew 不超出 [ x min , x max ] \left\lbrack {{x}_{\text{min}},{x}_{\text{max}}}\right\rbrack [xmin,xmax]

2. 主程序核心板块详解

(1)参数设置(关键!直接影响SA性能)

问题参数

  • n n n :解的维度(此处设为2,便于二维可视化;高维问题需修改可视化代码)。

x min ⁡ , x max ⁡ {x}_{\min },{x}_{\max } xmin,xmax :搜索空间边界(根据目标函数特性调整, Sphere函数在 [ 5 , 5 ] \left\lbrack {5,5}\right\rbrack [5,5] 内足够包含最小值)。

算法参数

T 0 {T}_{0} T0 (初始温度):需足够高以保证初始接受概率。例如,若初始解与较差解的 δ = 100 \delta = {100} δ=100 , T 0 = 100 {T}_{0} = {100} T0=100 时接受概率 P = exp ⁡ ( 100 / 100 ) = 0.3679 P = \exp \left( {{100}/{100}}\right) = {0.3679} P=exp(100/100)=0.3679 ,仍有一定概率接受差解以探索新区域。

α \alpha α (降温系数):典型取值0.85~0.98。 α = 0.95 \alpha = {0.95} α=0.95 表示每次降温保留95%的温度,平衡搜索效率与精度。

L L L (马尔可夫链长度):每个温度下迭代次数。 L = 100 L = {100} L=100 表示在当前温度下充分采样邻域,避免未平衡就降温。

T f {T}_{f} Tf (停止温度):需足够低以确保收敛。 T f = 1 e 5 {T}_{f} = {1e} 5 Tf=1e5 时,对 δ = 1 \delta = 1 δ=1 的差解,接受概率 P = exp ⁡ ( 1 / 1 e 5 ) ≈ 0 P = \exp \left( {1/{1e} 5}\right) \approx 0 P=exp(1/1e5)0 ,系统冻结。

(2)初始化

初始解生成:通过x_min + (x_maxx_min)*rand(n,1)在搜索空间内随机采样,避免初始解陷入局部最优区域。

最优解跟踪:用x_best和f_best记录迭代过程中的全局最优,避免因接受差解而丢失历史最优。

(3)模拟退火迭代(核心机制)

温度循环:从 T 0 {T}_{0} T0 开始,按 T = α ⋅ T T = \alpha \cdot T T=αT 降温,直至 T < T f T < {T}_{f} T<Tf

平衡过程(内循环):每个温度下迭代 L L L 次,通过Metropolis准则接受/拒绝邻域解:

  • 若新解更优 ( δ = f new f current < 0 ) \left( {\delta = {f}_{\text{new}} {f}_{\text{current}} < 0}\right) (δ=fnewfcurrent<0) : 直接接受,更新当前解和全局最优。

若新解较差( δ ≥ 0 \delta \geq 0 δ0 ):以概率 P = exp ⁡ ( δ / T ) P = \exp \left( {\delta /T}\right) P=exp(δ/T) 接受,模拟物理系统热运动的随机性,避免过早收敛到局部最优。

(4)结果输出与可视化

数值输出:打印最优解 x best {\mathbf{x}}_{\text{best}} xbest 和最优函数值 f best {f}_{\text{best}} fbest ,验证是否接近理论最小值。

等高线图:直观展示优化结果(蓝色圆点)与理论最小值(红色星号)的位置关系,验证解的合理性。

收敛曲线:展示 f best {f}_{\text{best}} fbest 随温度迭代的下降过程,判断算法是否收敛(曲线趋于平缓表明收敛)。

3. 关键参数调优建议

初始温度 T 0 {T}_{0} T0 :若算法过早收敛到局部最优,需增大 T 0 {T}_{0} T0 (如200);若收敛过慢,可减小 T 0 {T}_{0} T0 (如50)。

降温系数 α \alpha α : 高维度/复杂函数需更小的 α \alpha α (如0.98) 以增加搜索时间;简单函数可用0.9(降温更快)。

  • 马尔可夫链长度L:解维度越高, L L L 需越大(如n=10时设 L = 200 L = {200} L=200 ),确保当前温度充分探索邻域。

运行效果验证

  • 最优解: x b e s t {\mathbf{x}}_{\mathrm{{best}}} xbest 接近(0,0), f b e s t {f}_{\mathrm{{best}}} fbest 接近0(如1 e 4 e 4 e4 量级,受数值精度和参数影响)。

  • 收敛曲线:随温度迭代, f b e s t {f}_{\mathrm{{best}}} fbest 从初始值(如20~50)快速下降,最终趋于稳定。

等高线图:蓝色圆点(SA结果)与红色星号(理论最小值)几乎重合,验证算法有效性。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐