一、 模拟退火算法
模拟退火算法是一种启发式随机搜索算法,主要用于在大规模搜索空间中寻找全局最优解或近似最优解。它特别适用于解决组合优化问题,如旅行商问题(TSP)、图着色、调度问题等。
它的核心思想来源于固体物理中的退火过程:
-
物理退火过程:
- 将固体加热至充分高的温度,使其内部粒子(原子、分子)处于高能活跃状态。
- 然后缓慢降温(退火)。在降温过程中,粒子有足够的时间在每个温度下探索其状态,并趋向于能量更低的状态。
- 如果降温足够缓慢,固体最终会达到能量最低的基态(全局最优)。
-
算法类比:
- 温度 (Temperature):控制算法接受“劣解”(能量更高解)的概率。初始温度高,接受劣解的概率大,利于跳出局部最优;随着温度降低,接受劣解的概率减小,算法逐渐收敛。
- 状态 (State):问题的一个可能解(例如,TSP 中的一个城市访问顺序)。
- 能量 (Energy):状态对应的目标函数值(例如,TSP 中的总路径长度)。目标是最小化能量。
- 状态转移:从当前状态产生一个新状态(例如,对城市序列进行交换、反转等操作)。
- Metropolis 准则:决定是否接受新状态的核心规则。
- 如果新状态的能量
E_new小于当前状态的能量E_current,则总是接受新状态。 - 如果新状态的能量
E_new大于当前状态的能量E_current,则以一定的概率接受新状态:
P = exp(-(E_new - E_current) / T)
其中T是当前温度。能量差越大或温度越低,接受概率越小。
- 如果新状态的能量
-
算法步骤:
- 初始化:选择一个初始解
S_current,设定初始温度T0(足够高),终止温度T_end(接近 0),降温系数alpha(如 0.99),每个温度下的迭代次数L。 - 循环(当
T > T_end时):- 在当前温度
T下,重复L次:
a. 生成新解:对S_current进行微小扰动,得到新解S_new。
b. 计算能量差:ΔE = E(S_new) - E(S_current)。
c. Metropolis 准则:
* 如果ΔE < 0,则接受S_new为S_current。
* 如果ΔE >= 0,则生成随机数r ∈ [0, 1),如果r < exp(-ΔE / T),则接受S_new为S_current。
d. 更新最优解:如果E(S_new) < E(S_best),则更新S_best = S_new。 - 降温:
T = alpha * T。
- 在当前温度
- 输出:返回找到的最优解
S_best。
- 初始化:选择一个初始解
-
关键参数:
- 初始温度
T0:应足够高,使得初始阶段几乎可以接受任何移动(接受概率接近 1)。 - 终止温度
T_end:足够低,使得算法基本不再接受劣解。 - 降温系数
alpha:控制降温速度。通常取 0.8 ~ 0.99。alpha越接近 1,降温越慢,搜索越充分,但耗时越长。 - 每个温度下的迭代次数
L:决定了在每个温度下探索的广度。L越大,越可能找到该温度下的好解,但耗时增加。 - 邻域结构 (Neighborhood Structure):如何从当前解生成新解。好的邻域结构能有效探索解空间。
- 初始温度
二、 C++ 实现:以“求函数最小值”为例
我们以寻找一个简单函数的全局最小值为例。考虑函数:
f(x) = x * sin(10 * π * x) + 2.0,定义域 x ∈ [-1, 2]。
这个函数有很多局部极小值,容易陷入局部最优。
#include <iostream>
#include <vector>
#include <cmath>
#include <random>
#include <algorithm>
// 目标函数:求其最小值
double objectiveFunction(double x) {
return x * std::sin(10 * M_PI * x) + 2.0;
}
// 模拟退火算法
double simulatedAnnealing(double x_min, double x_max) {
// 1. 初始化
std::random_device rd;
std::mt19937 gen(rd()); // 随机数生成器
std::uniform_real_distribution<double> dis_x(x_min, x_max); // 用于生成初始解
std::uniform_real_distribution<double> dis_p(0.0, 1.0); // 用于生成概率
std::uniform_real_distribution<double> dis_step(-0.5, 0.5); // 用于生成扰动步长(可调整范围)
// 初始解
double current_x = dis_x(gen);
double current_energy = objectiveFunction(current_x);
// 最优解
double best_x = current_x;
double best_energy = current_energy;
// 参数设置
double T = 1000.0; // 初始温度
double T_end = 1e-8; // 终止温度
double alpha = 0.99; // 降温系数
int L = 100; // 每个温度下的迭代次数
// 2. 主循环
while (T > T_end) {
for (int i = 0; i < L; ++i) {
// a. 生成新解:在当前解附近随机扰动
double delta = dis_step(gen); // 随机步长
double new_x = current_x + delta;
// 确保新解在定义域内
new_x = std::max(x_min, std::min(x_max, new_x));
double new_energy = objectiveFunction(new_x);
// b. 计算能量差
double delta_E = new_energy - current_energy;
// c. Metropolis 准则
bool accept = false;
if (delta_E < 0) {
// 优于当前解,总是接受
accept = true;
} else {
// 劣于当前解,以概率 exp(-delta_E / T) 接受
double probability = std::exp(-delta_E / T);
if (dis_p(gen) < probability) {
accept = true;
}
}
if (accept) {
current_x = new_x;
current_energy = new_energy;
// d. 更新全局最优解
if (new_energy < best_energy) {
best_x = new_x;
best_energy = new_energy;
}
}
}
// 降温
T *= alpha;
}
return best_x; // 返回找到的最优解的 x 值
}
int main() {
double x_min = -1.0;
double x_max = 2.0;
// 运行模拟退火算法
double best_x = simulatedAnnealing(x_min, x_max);
double best_energy = objectiveFunction(best_x);
std::cout << "Found minimum at x = " << best_x << std::endl;
std::cout << "Minimum value f(x) = " << best_energy << std::endl;
return 0;
}
三、 代码说明
objectiveFunction: 定义了我们要最小化的目标函数。- 随机数生成: 使用 C++11 的
<random>库生成高质量的随机数,包括初始解、接受概率判断和扰动步长。 - 解的生成:
new_x = current_x + delta是一种简单的邻域结构(在当前位置附近随机移动)。dis_step的范围(-0.5, 0.5)可以根据问题调整。 - 边界处理:
std::max(x_min, std::min(x_max, new_x))确保新解不会超出定义域。 - Metropolis 准则: 核心逻辑,决定是否接受新解。
- 最优解更新: 在每次接受新解后,都检查是否优于历史最优解。
- 降温:
T *= alpha实现几何降温。
四、 注意事项与扩展
- 参数调优: 模拟退火的性能高度依赖于参数(
T0,T_end,alpha,L)和邻域结构。需要根据具体问题进行实验调整。 - 邻域结构: 对于复杂问题(如 TSP),邻域结构的设计至关重要(如 2-opt, 3-opt, 交换等)。
- 收敛性: 理论上,如果降温足够慢(如
T = T0 / log(k)),模拟退火可以收敛到全局最优。但实践中通常使用更快的降温方式(如几何降温)以获得可接受的解。 - 适用性: 非常适合 NP-hard 问题或具有大量局部最优的连续/离散优化问题。
- 缺点: 需要调参,收敛速度可能较慢,结果具有随机性(多次运行结果可能不同)。
2万+

被折叠的 条评论
为什么被折叠?



