[模板]模拟退火/SCOI传送带

模拟退火

(Generated by deepseek-r1)
模拟退火算法是一种基于物理退火过程的启发式优化算法,其核心思想是通过概率性接受次优解来跳出局部最优,逐步逼近全局最优解。以下是其核心流程的简要讲解:


  1. 初始化参数
  • 初始温度 $ T_0 $:设置较高温度(如 $ 10^5 $ ),确保算法在早期有足够能量跳出局部最优。
  • 初始解:随机生成或启发式生成一个初始解(如随机路径、参数组合)。
  • 降温系数 $ \alpha $(如 0.95~0.99):控制温度下降速度,通常接近 1 以保证缓慢降温。
  • 终止条件:设定最低温度 $ T_{\text{min}} $ 或最大迭代次数。

  1. 迭代过程(外循环)
    在每个温度 $ T $ 下,执行以下步骤直到满足终止条件:
    (1) 内循环(状态扰动与评估)
  • 生成新解:通过随机扰动当前解(如交换路径中的两个点、调整参数值)产生候选解。
  • 计算目标函数差 $ \Delta E $:比较新解与当前解的目标函数值差(例如路径长度、误差值)。

(2) Metropolis 接受准则

  • 接受更优解:若 $ \Delta E < 0 $(新解更优),直接接受新解。
  • 概率性接受劣解:若 $ \Delta E \geq 0 $,以概率 $ P = \exp(-\Delta E / T) $ 接受新解。通过生成随机数与 $ P $ 比较决定是否接受劣解,避免陷入局部最优

(3) 降温

  • 温度更新:按降温函数降低温度,例如指数降温 $ T_{k+1} = \alpha \cdot T_k $。

  1. 终止与输出
  • 终止条件:当温度降至 $ T_{\text{min}} $ 或目标函数值不再显著变化时结束算法。
  • 输出最优解:返回当前记录的最优解。

关键特点

  • 概率性搜索:通过接受劣解的策略,平衡全局探索(高温阶段)与局部开发(低温阶段)。
  • 参数敏感:初始温度、降温速率、内循环次数等参数需根据问题调整,直接影响收敛速度和结果质量。

应用场景

  • 组合优化:如旅行商问题(TSP)、调度问题。
  • 连续优化:如函数极值求解、神经网络训练。

通过上述流程,模拟退火能够在复杂解空间中高效搜索全局最优解,适用于多种非凸优化问题。

总的来说原理是对的,但是解题时容易出现玄学错误,使用IOI这种赛制的比赛中

SCOI传送带

不得不说有AI还是很方便,注释都不用写
连代码AI生成了都是一遍过

点击查看代码
#include<bits/stdc++.h> 
 
using namespace std; 
 
// 定义一个类型别名pdd,它是一个pair<double, double>类型,用于表示二维平面上的点 
typedef pair<double,double> pdd; 
 
// 定义宏x和y,分别表示pair的第一个和第二个元素,方便访问点的坐标 
#define x first 
#define y second 
 
// 计算两点之间的欧几里得距离 
// 参数a和b是两个pdd类型的点 
// 返回值是两点之间的距离 
double dis(pdd a, pdd b) { 
    // 根据欧几里得距离公式计算距离,即sqrt((x1 - x2)^2 + (y1 - y2)^2) 
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); 
} 
 
// 计算从点a到点b,点c到点d的路径上,按照一定比例移动所需的总时间 
// 参数a和b表示第一条线段的起点和终点 
// 参数c和d表示第二条线段的起点和终点 
// 参数t表示在第一条线段上移动的比例 
// 参数s表示在第二条线段上移动的比例 
// 参数p表示在第一条线段上的移动速度 
// 参数q表示在第二条线段上的移动速度 
// 参数r表示从第一条线段上的某点到第二条线段上某点的移动速度 
// 返回值是总时间 
double caltime(pdd a, pdd b, pdd c, pdd d, double t, double s, double p, double q, double r) { 
    pdd e, f; 
    // 计算第一条线段上按照比例t的点e的坐标 
    e.x = a.x + (b.x - a.x) * t; 
    e.y = a.y + (b.y - a.y) * t; 
    // 计算第二条线段上按照比例s的点f的坐标 
    f.x = c.x + (d.x - c.x) * s; 
    f.y = c.y + (d.y - c.y) * s; 
    // 根据时间 = 距离 / 速度,计算总时间 
    return dis(a, e) / p + dis(e, f) / r + dis(f, d) / q; 
} 
 
// 生成一个[0, 1]之间的随机小数 
// 返回值是一个[0, 1]之间的随机小数 
double pro() { 
    // rand()函数生成一个随机整数,除以RAND_MAX得到一个[0, 1]之间的随机小数 
    return (double)rand() / RAND_MAX; 
} 
 
// 使用模拟退火算法求解从点a到点b,点c到点d的最短时间路径 
// 参数a和b表示第一条线段的起点和终点 
// 参数c和d表示第二条线段的起点和终点 
// 参数p表示在第一条线段上的移动速度 
// 参数q表示在第二条线段上的移动速度 
// 参数r表示从第一条线段上的某点到第二条线段上某点的移动速度 
// 返回值是最短时间 
double solve(pdd a, pdd b, pdd c, pdd d, double p, double q, double r) { 
    // 随机初始化第一条线段和第二条线段上的移动比例t和s 
    double t = pro(), s = pro(); 
    // 计算初始的总时间 
    double best = caltime(a, b, c, d, t, s, p, q, r); 
    // 模拟退火算法的初始温度 
    for (double temp = 1e5; temp > 1e-5; temp *= 0.999) { 
        // 在当前t的基础上,随机扰动得到新的t值,并确保在[0, 1]范围内 
        double nt = max(0.0, min(1.0, t + pro() * 0.2 - 0.1)); 
        // 在当前s的基础上,随机扰动得到新的s值,并确保在[0, 1]范围内 
        double ns = max(0.0, min(1.0, s + pro() * 0.2 - 0.1)); 
        // 计算新的总时间 
        double ntime = caltime(a, b, c, d, nt, ns, p, q, r); 
        // 如果新的总时间更短,或者满足一定的概率接受更差的解(模拟退火的特性) 
        if (ntime < best || exp(best - ntime) / temp > pro()) { 
            // 更新t和s的值 
            t = nt; 
            s = ns; 
            // 更新最短时间 
            best = min(best, ntime); 
        } 
    } 
    return best; 
} 
 
int main() { 
    // 初始化随机数种子,使用当前时间作为种子,确保每次运行程序时生成的随机数不同 
    srand(time(0)); 
 
    pdd a, b, c, d; 
    double p, q, r; 
    // 从标准输入读取第一条线段的起点和终点的坐标 
    cin >> a.x >> a.y >> b.x >> b.y; 
    // 从标准输入读取第二条线段的起点和终点的坐标 
    cin >> c.x >> c.y >> d.x >> d.y; 
    // 从标准输入读取三条路径的移动速度 
    cin >> p >> q >> r; 
 
    // 输出最短时间,保留两位小数 
    cout << fixed << setprecision(2) << solve(a, b, c, d, p, q, r) << endl; 
    return 0; 
} 
posted @ 2025-04-04 16:29  归游  阅读(83)  评论(0)    收藏  举报