【搜索】退火模拟算法

一、 模拟退火算法

模拟退火算法是一种启发式随机搜索算法,主要用于在大规模搜索空间中寻找全局最优解近似最优解。它特别适用于解决组合优化问题,如旅行商问题(TSP)、图着色、调度问题等。

它的核心思想来源于固体物理中的退火过程

  1. 物理退火过程

    • 将固体加热至充分高的温度,使其内部粒子(原子、分子)处于高能活跃状态。
    • 然后缓慢降温(退火)。在降温过程中,粒子有足够的时间在每个温度下探索其状态,并趋向于能量更低的状态。
    • 如果降温足够缓慢,固体最终会达到能量最低基态(全局最优)。
  2. 算法类比

    • 温度 (Temperature):控制算法接受“劣解”(能量更高解)的概率。初始温度高,接受劣解的概率大,利于跳出局部最优;随着温度降低,接受劣解的概率减小,算法逐渐收敛。
    • 状态 (State):问题的一个可能解(例如,TSP 中的一个城市访问顺序)。
    • 能量 (Energy):状态对应的目标函数值(例如,TSP 中的总路径长度)。目标是最小化能量。
    • 状态转移:从当前状态产生一个新状态(例如,对城市序列进行交换、反转等操作)。
    • Metropolis 准则:决定是否接受新状态的核心规则。
      • 如果新状态的能量 E_new 小于当前状态的能量 E_current,则总是接受新状态。
      • 如果新状态的能量 E_new 大于当前状态的能量 E_current,则以一定的概率接受新状态:
        P = exp(-(E_new - E_current) / T)
        其中 T 是当前温度。能量差越大或温度越低,接受概率越小。
  3. 算法步骤

    1. 初始化:选择一个初始解 S_current,设定初始温度 T0(足够高),终止温度 T_end(接近 0),降温系数 alpha(如 0.99),每个温度下的迭代次数 L
    2. 循环(当 T > T_end 时):
      • 在当前温度 T 下,重复 L 次:
        a. 生成新解:对 S_current 进行微小扰动,得到新解 S_new
        b. 计算能量差ΔE = E(S_new) - E(S_current)
        c. Metropolis 准则
        * 如果 ΔE < 0,则接受 S_newS_current
        * 如果 ΔE >= 0,则生成随机数 r ∈ [0, 1),如果 r < exp(-ΔE / T),则接受 S_newS_current
        d. 更新最优解:如果 E(S_new) < E(S_best),则更新 S_best = S_new
      • 降温T = alpha * T
    3. 输出:返回找到的最优解 S_best
  4. 关键参数

    • 初始温度 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;
}

三、 代码说明

  1. objectiveFunction: 定义了我们要最小化的目标函数。
  2. 随机数生成: 使用 C++11 的 <random> 库生成高质量的随机数,包括初始解、接受概率判断和扰动步长。
  3. 解的生成: new_x = current_x + delta 是一种简单的邻域结构(在当前位置附近随机移动)。dis_step 的范围(-0.5, 0.5)可以根据问题调整。
  4. 边界处理: std::max(x_min, std::min(x_max, new_x)) 确保新解不会超出定义域。
  5. Metropolis 准则: 核心逻辑,决定是否接受新解。
  6. 最优解更新: 在每次接受新解后,都检查是否优于历史最优解。
  7. 降温: T *= alpha 实现几何降温。

四、 注意事项与扩展

  • 参数调优: 模拟退火的性能高度依赖于参数(T0, T_end, alpha, L)和邻域结构。需要根据具体问题进行实验调整。
  • 邻域结构: 对于复杂问题(如 TSP),邻域结构的设计至关重要(如 2-opt, 3-opt, 交换等)。
  • 收敛性: 理论上,如果降温足够慢(如 T = T0 / log(k)),模拟退火可以收敛到全局最优。但实践中通常使用更快的降温方式(如几何降温)以获得可接受的解。
  • 适用性: 非常适合 NP-hard 问题或具有大量局部最优的连续/离散优化问题。
  • 缺点: 需要调参,收敛速度可能较慢,结果具有随机性(多次运行结果可能不同)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值