2025全国大学生数学建模C题思路模型代码论文(开赛后第一时间更新),备战国赛,算法解析——模拟退火算法
模拟退火就像一位聪明的探险者:出发时 (高温) : 精力旺盛,敢走崎岖山路 (接受差解) ,探索大范围地图;途中(中温):体力下降,只敢走缓坡(低概率接受差解),缩小搜索范围;终点(低温):精疲力尽,只走下坡路(只接受优解),最终找到最低山谷(全局最优)。记住核心公式:接受差解的概率PexpΔfTPexpΔfT,温度越高、差解越“不差”(Δf越小),接受概率越大!作用:作为优化目标,验证算法全局
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准则 决定,它是模拟退火“跳出局部最优”的关键!
分两种情况判断:
- 若 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′ (就像下坡路,肯定要走)。
- 若 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}}} T≤Tend (比如 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} P≈0.8∼0.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} 105∼103 (比如 10 4 {10}^{4} 104 )。
衰减系数 α \alpha α : 0.85 ∼ 0.98 {0.85} \sim {0.98} 0.85∼0.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 次”):
- 生成邻域解(试新解):
例:当前解 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] 。
-
计算目标函数: f ( x ′ ) = 95 f\left( {x}^{\prime }\right) = {95} f(x′)=95 km(比当前解100 km更优)。
-
接受判断(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(0∼1 之间) , 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′ 。
- 更新最优解:
若当前解 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 。
- 降温:内循环 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}}} T≤Tend 时,输出最优解 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)=A⋅n+i=1∑n(xi2A⋅cos(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 + A∗n+ 求和 ( x _ i 2 A ∗ cos ( 2 π x _ i ) ) \left( {x\_ {i}^{2} {A}^{ * }\cos \left( {{2\pi x}\_ i}\right) }\right) (x_i2A∗cos(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.01∼0.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):
-
邻域搜索:生成邻域解并截断到搜索空间内(np.clip),避免解超出边界。
-
能量计算:计算邻域解的目标函数值(能量)。
-
Metropolis准则:
若邻域解更优(delta = current_value neighbor_value > 0),直接接受。
若邻域解较差(delta < 0),按概率exp(delta/temp)接受(温度越高,接受概率越大, 允许“跳出”局部最优)。
-
最优解更新:若当前解优于历史最优,更新全局最优解和最优值。
-
历史记录: 将当前最优值存入列表,用于后续可视化。
-
温度衰减: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=1∑nxi2 。
特点:凸函数,全局最小值唯一 ( 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
功能:生成当前解的邻域解(局部搜索),确保新解在搜索空间内。
核心逻辑:
-
扰动步长:设为搜索范围的2% (step = 0.02*(x_maxx_min)) ,平衡局部探索精度与全局搜索范围。
-
随机扰动:在当前解 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 ]的均匀随机扰动。
-
边界约束:通过逻辑索引强制新解 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结果)与红色星号(理论最小值)几乎重合,验证算法有效性。
更多推荐
所有评论(0)