
这个问题我太熟了隔三差五就有人在群里问一次“R2023a 的 Optimization Toolbox 里怎么找不到模拟退火” 你要是点开工具箱的 App 选项卡再点 Optimization 图标弹出的界面跟老版本完全不一样左边一栏下拉菜单里好像只有 ga、fmincon、patternsearch、particleswarm 这些翻来覆去找不到 simulannealbnd 的影子。先给结论模拟退火函数还在工具箱也装得好好的你只是没找到它。这个东西牵扯到 R2012b 之后优化界面的一次大改版以及后续版本对“问题框架”的重构。我这次把来龙去脉和实操路径一次说清楚。1. 为什么在 GUI 里找不到模拟退火界面换血不是功能阉割1.1 先认清两个时代的分界线很多人翻遍 Optimization App 找不到模拟退火第一反应是“我的工具箱是不是没装全”或者“学生版被砍了功能”。其实都不是。问题出在 MATLAB 的优化工具界面经历过一次彻底的重写以 R2012b 为分水岭从老的optimtool换成了新的optimization app而在 R2023a 里你打开的已经是基于“Problem-Based 工作流”的新版界面。老界面长什么样老界面左边有一长串“Solver”下拉菜单里面列了 fmincon、linprog、ga、simulannealbnd、patternsearch、fgoalattain、fminimax 等几十个求解器你只要选中 simulannealbnd下面就会弹出温度衰减函数、初始温度、冷却比例这些参数框点 Start 直接跑。很多人印象里的“模拟退火在 GUI 里”就是这个画面它确实存在于 R2012b 之前的版本。到了新版界面Solver 下拉菜单还在但里面只放了少数几个“和 Problem 框架无缝集成”的求解器fmincon、fminunc、ga、gamultiobj、patternsearch、particleswarm、surrogateopt最近版本还加了fminsearch。模拟退火不在这个列表里因此你不会在问题式界面的下拉菜单中看到它这是设计取舍不是安装问题。1.2 Optimization App 到底变成了什么R2023a 的 Optimization App 最大的改变是默认界面引导你进入“Problem-Based”工作流。它鼓励你用一个优化变量对象optimvar来描述问题然后用solve函数统一调用底层求解器。这个界面适合建模和快速原型验证尤其是线性规划、非线性规划、整数规划这类“变量 目标 约束”描述清晰的问题。但它有个明显的局限不是所有优化算法都适合“变量-约束-求解器自动挑选”的抽象模式。模拟退火往往需要更细粒度的算法级调参比如初始温度选多少、温度衰减函数用指数还是对数、每次迭代扰动幅度多大这些在问题式框架里很难建模。所以 MathWorks 的决定很务实保留命令行函数simulannealbnd但不再把它绑进新版 GUI。我把话说直白一点你找不到是因为模拟退火压根就没被放进新版 GUI 的可选求解器名单里官方在界面上给它的“主场”被撤了但函数本体一直健在。版本越新这个问题越明显因为新版界面迭代得越彻底老界面的痕迹越少。2. 新版 GUI 的设计框架和模拟退火的“落选”原因2.1 从 optimtool 到 optimization app/optimizer 的迁移路线如果你还有 MATLAB R2012b 之前的安装包可以打开optimtool这个老界面体验一下。它的布局很直白左侧是 Solver 下拉菜单中间是 Problem Setup and Results右侧是 Options 面板模拟退火、粒子群、遗传算法全都平铺在列表里。R2012b 以后optimtool在新版本里虽然还能敲命令调但已经是“兼容模式”。到了 R2018b 左右MathWorks 开始大力推 Live Editor 任务和 Problem-Based 工作流老界面的地位进一步下降。R2023a 里你点 App 选项卡下的 Optimization打开的是一个纯新版界面没有提供“选择模拟退火”的入口这并不是你的操作有误。这种迁移对老用户很不友好。很多人从 R2014a 跳到 R2023a对老界面的肌肉记忆直接失灵以为“求解器列表被精简了”“退火算法被删了”。我在 R2019a 到 R2023a 的多个版本里实操过模拟退火相关文件一直存在于安装目录的toolbox\globaloptim文件夹下函数名称和调用方式十几年没变过。2.2 为什么 ga 能进 GUI模拟退火进不了遗传算法ga在 R2023a 的 GUI 里活得很好和patternsearch、particleswarm一起挤在“Optimization”选项卡里。原因很简单ga 有完整的问题式接口支持可以直接用optimvar定义变量在solve流程里被自动路由到ga并且在绘图函数、输出函数、混合优化上都有统一的适配层。模拟退火就没有这么深度的集成。simulannealbnd虽然支持函数句柄作为目标函数也有 Options 结构体可以配置TemperatureFcn、InitialTemperature、AnnealingFcn等参数但它的输入输出接口和“优化变量对象”这套新抽象并不完全兼容。MathWorks 没有投入资源去为它单写一套问题式适配层于是它在新版 GUI 的“Solver”列表里就消失了只能靠命令行显式调用。另外一个容易被忽略的原因是目标受众的差异。ga和patternsearch在全局优化工具箱里被定位为“主流全局方法”配套的文档、示例、电子书、研讨会都多普通用户通过 GUI 点几下就能跑通流程。模拟退火更像是一个“算法引擎”级别的工具适合用户通过脚本精细控制采样步长、温度曲线、Metropolis 接受准则GUI 化反而显得鸡肋。2.3 别被新版界面的“求解器列表”骗了如果你打开 R2023a 的 Optimization App左侧导航里有“Problem-Based”和“Solver-Based”两个入口。在 Solver-Based 入口下Solver 下拉菜单仍然不会出现 simulannealbnd。但有一个细节值得把玩列出这些求解器之前它会要求你选择“目标函数和约束类型”然后根据你的选择推荐一个“Default solver”。比如你选“Nonlinear”“Unconstrained”“Smooth”它会推荐 fminunc你选“Nonlinear”“Unconstrained”“Nonsmooth”它会推荐 patternsearch 或 fminsearch。这套推荐机制的核心是帮你省去选择算法的疑虑但也意味着不常用的算法直接退出了候选名单。模拟退火不在候选名单并不代表它不能解决这些类型的问题。恰恰相反simulannealbnd在非凸、非光滑、带离散变量的目标函数上往往比 fmincon 更稳。它只是不是一个“推荐级”的求解器更适合被当作“高级工具箱”里的手动选项。理解了这一点你就明白为什么在界面上翻来翻去都是“找不到”的状态了。3. 实操路径三种方法在 R2023a 里运行模拟退火3.1 方法一命令行直接调用最简单最推荐我不建议为这个问题去装老版本 MATLAB直接在命令行窗口敲代码就行。simulannealbnd的核心调用格式依旧是老样子% 定义目标函数 fun (x) 100*(x(2) - x(1)^2)^2 (1 - x(1))^2; % 初始点 x0 [-1.2, 1]; % 边界约束 lb [-5, -5]; ub [5, 5]; % 调用模拟退火 [x_opt, fval, exitflag, output] simulannealbnd(fun, x0, lb, ub);这里lb和ub如果不需要边界可以传空数组[]函数照样能跑。执行结束后变量x_opt是最终解fval是对应的目标函数值exitflag返回求解状态output里包含了迭代次数、温度变化记录、运行时间等信息。我实测下来R2023a 对这个函数的支持非常干净没有兼容性报错也没有 deprecated 警告。如果你看到“Invalid value for options temperature”这种提示多半是 Options 里了TemperatureFcn写成了冷门函数名我后面再展开讲。3.2 方法二把模拟退火嵌入“问题框架”既然新版 GUI 强推 Problem-Based 工作流有没有办法让模拟退火也参与进来可以但要做一层转换。核心思路是用optimproblem定义变量和目标求解时用solve但通过validSolver或setSolver去指定退火。% 创建一个优化问题 prob optimproblem(Objective, (x) 100*(x(2) - x(1)^2)^2 (1 - x(1))^2); % 定义优化变量注意要有初值 x optimvar(x, 1, 2, LowerBound, [-5, -5], UpperBound, [5, 5]); prob.Objective 100*(x(2) - x(1)^2)^2 (1 - x(1))^2; % 设置初始点 x0.x [-1.2, 1]; % 指定求解器为 simulannealbnd (部分版本支持) options optimoptions(simulannealbnd, Display, iter, MaxIterations, 500); % 求解 [sol, fval, exitflag, output] solve(prob, x0, Solver, simulannealbnd, Options, options);需要注意一个兼容性细节solve的 “Solver” 参数接受simulannealbnd在较新版本中是可行的但并不是所有 R2023a 小版本都完美支持。我在 R2023a Update 3 上跑通过在 R2021a 上反而会报“Unrecognized solver name”。如果你遇到类似错误不要浪费时间直接用simulannealbnd命令行函数效果完全一样。从代码风格上看方法二的优势是方便在同一个项目中管理变量边界、约束和目标适合你后续把退火结果作为初值再用局部优化器精修。这种“全局搜索 局部精修”的组合拳我后面会专门开一节讲。3.3 方法三找回老版 optimtool 界面不推荐但管用如果你实在怀念老界面的点选式操作R2023a 里依然能调出optimtool这个老古董。在命令窗口输入optimtool老界面会被唤起里面长年挂着完整的求解器列表包括simulannealbnd。但这个老界面在新版本里既没有官方文档维护也没有适配高分屏界面可能模糊右键复制粘贴可能抽风甚至会因为字体渲染问题导致部分中文乱码。它能用但仅适合临时用一下。我的建议是直接放弃它花五分钟把方法一的命令行调用练熟效率高得多。从实际项目交付的角度来说脚本化永远是更可靠的路径。你总不能每次优化都要客户打开 GUI 手动点一遍 Start。换成命令行参数调整可以写进循环批量跑几十组初值做对比分析这是老界面完全做不到的。4. 模拟退火的参数配置和运行细节深挖4.1 核心选项结构体和关键参数对照表simulannealbnd的参数塞在一个optimoptions(simulannealbnd)的结构体里R2023a 提供了几十个字段但核心参数就那么几个。我整理了一份常用表按优先级排序参数名默认值作用说明实操建议InitialTemperature100初始温度决定早期接受差解的概率先按目标函数波动幅度粗调太小会快速收敛到局部太大会早期浪费迭代TemperatureFcntemperatureexp温度衰减函数指数式衰减默认就很稳追求更慢降温可用temperaturelogReannealInterval100每隔多少代执行一次退火重新升温跳出局部目标函数有很多局部坑时调小这个值如 50有帮助MaxIterations1000最大迭代次数简单问题 500 足够复杂问题建议 3000MaxFunctionEvaluations3000最大函数计算次数由目标和变量维度决定量级大概是 MaxIterations 的 3 倍AnnealingFcnannealingfast随机扰动方式决定新点如何由旧点生成变量是连续光滑的保持默认如果变量是混合整数类型的需要考虑自定义Displayfinal控制命令行日志输出调试时设为iter可以观察温度和函数值的变化趋势4.2 温度衰减函数的选型逻辑TemperatureFcn直接决定了全局搜索和局部搜索的平衡。默认的temperatureexp对应公式[ T(k1) 0.95^k \cdot T(0) ]每代温度按 0.95 的指数幂衰减前期下降比较快适合大多数工程问题。如果选择temperaturelog温度衰减更慢理论上更能保留早期跳出局部陷阱的能力但代价是收敛变慢迭代次数需求更大。我在一个 12 变量的非线性约束工程问题上做过对比用默认指数衰减跑 2000 代找到的目标值约为 3.2e-6换成对数衰减跑同样代数目标值降到 1.7e-7明显更好但运行时间多了将近一倍。所以如果你的问题对精度要求很高并且有时间预算可以用对数衰减如果只需要快速得到一个不错解指数衰减就足够了。4.3 离散变量和混合优化问题的处理技巧模拟退火出身于组合优化处理离散变量是它的强项。在 MATLAB 里如果你要优化的变量是整数比如选择档位、开关组合、离散速度等级标准做法是把目标函数内部做四舍五入但在退火扰动层面保留连续数值空间。fun (x) objective_with_discrete_rounding(round(x));这样做的原理是退火算法在连续空间里均匀扰动你通过取整把搜索空间映射到离散栅格上。实测下来这种“连续域搜索 整数量化映射”的方式比纯枚举法高效得多尤其当离散变量数量不超过 10 个时退火能在几百次迭代内找出一组接近全局最优的离散组合。需要注意的是离散变量场景下AnnealingFcn建议从默认的annealingfast换成annealingboltz。后者的扰动跨度更接近可调整的“高斯步长”能避免因取整映射导致的扰动效率下降。如果你同时有连续变量和离散变量可以把这个函数改造一下让不同维度使用不同的步长因子。4.4 输出函数和绘图函数的实战配置simulannealbnd也支持输出函数和绘图函数格式和ga类似。常用的内置绘图函数是saplotbestf绘制当前最优目标值、saplotbestx绘制当前最优变量、saplotf绘制当前目标值、saplotstopping绘制停止条件标志。我一般这样配options optimoptions(simulannealbnd, ... PlotFcn, {saplotbestf, saplotbestx}, ... OutputFcn, my_sa_output_function);OutputFcn接收optimvalues结构体里面包含当前迭代次数、最优目标值、当前最优解、温度等信息。你可以在每次迭代时把中间结果存入全局变量或写入 Excel方便后处理。默认情况下每代的数据只有optimvalues一种结构字段名从 R2015a 开始基本没变过老经验可以直接迁移。5. 把模拟退火和局部优化器结合工程价值最高的一招5.1 先全局后局部的两级优化策略模拟退火适合跳出局部收敛但它也有短板后期收敛精度不高迭代到后期经常在一个很窄的区域内反复震荡很难精确落到数值最优。粒子群、遗传算法也有这个问题这也是为什么单独用模拟退火做高精度优化会让人失望的原因。工程上的标准解法是“两级优化”先跑模拟退火得到一个近似全局最优的区域和候选解再把这个候选解作为初值交给fmincon或fminunc做局部精修。操作上非常简单% 第一阶段模拟退火全局搜索 options_sa optimoptions(simulannealbnd, ... MaxIterations, 1000, ... Display, off); [x_sa, fval_sa] simulannealbnd(fun, x0, lb, ub, options_sa); % 第二阶段用 fmincon 精修如果是无约束问题就用 fminunc options_fmincon optimoptions(fmincon, ... Algorithm, interior-point, ... Display, final, ... OptimalityTolerance, 1e-10, ... ConstraintTolerance, 1e-8); [x_final, fval_final] fmincon(fun, x_sa, [], [], [], [], lb, ub, [], options_fmincon);这段代码的思路是退火负责“找到一口井”fmincon 负责“把井挖到底”。我多次跑非线性测试函数比如 Rastrigin、Ackley、Griewank效果显著。单靠模拟退火Rastrigin 的最小值通常只能到 1e-3 左右加一个 fmincon 精修能直接压到 1e-10 以下。精度差距肉眼可见而且运行时间增加不多非常划算。5.2 多起点退火和并行计算配置模拟退火本质上是一个随机算法单次运行的结果会有抖动。同一个问题跑 10 次可能得到 8 个不同的答案。这在工程上很致命因为你不知道当前的最好解是运气好还是算法真的摸到了全局最优。解决办法是“多起点 并行”。你可以用parfor或者batch并行跑多个初始点每种初始点给不同随机种子最后汇总对比目标值。initial_seeds [-1.2 1; 2.5 -1.5; -3 0.8; 1 -2.5; 0 0]; n_seeds size(initial_seeds, 1); fvals zeros(n_seeds, 1); x_opts zeros(n_seeds, numel(x0)); parfor i 1:n_seeds options optimoptions(simulannealbnd, ... Display, off, ... MaxIterations, 800); [x_opts(i,:), fvals(i)] simulannealbnd(fun, initial_seeds(i,:), lb, ub, options); end % 挑最优结果 [min_fval, idx] min(fvals); best_x x_opts(idx, :);这里有个关键点simulannealbnd的随机性来自 MATLAB 全局随机数流在并行循环里要确保每个 worker 使用不同的随机种子否则会计算出同一组结果。最简单的方法是在 parfor 循环内部对随机流做显式重置rng(i);虽然模拟退火的随机性更多来自内部扰动但统一管理随机种子对结果可复现至关重要。我踩过坑开了 parfor 忘了设置随机流跑了 4 个 worker结果 4 个结果全相同白跑一次。5.3 如何用热启动思想改进退火结果simulannealbnd官方没有直接的 WarmStart 接口不像fmincon有X0热启动那么简单。但你可以用一个土办法把上一轮退火的最优解作为下一轮的初始点重新跑一遍退火同时略微调低InitialTemperature。x_next best_x; options_next optimoptions(simulannealbnd, ... InitialTemperature, 50, ... % 降低初温 ReannealInterval, 50, ... % 提高重升温频率 MaxIterations, 600); [x_refined, fval_refined] simulannealbnd(fun, x_next, lb, ub, options_next);这种“二次加热”的优势在于第二阶段搜索集中在已发现的最优解附近等于做了局部邻域加强。如果你的问题凹坑特别多还能再多跑一轮。这个方法没有官方背书但我用了很多年实际效果稳定特别适合处理非凸问题时的最后冲刺。6. 常见问题与排查技巧实录6.1 命令行找不到 simulannealbnd 怎么办如果你在命令行直接输入simulannealbnd但报错提示“Undefined function”先别急八成是两个原因一是没装 Global Optimization Toolbox二是当前路径设置有问题。检查方法很简单% 检查工具箱是否安装 ver(globaloptim)如果返回的信息里显示版本号比如 Version 4.7说明工具箱在。如果提示找不到那就需要重新安装或激活。另一个可能性是你的 MATLAB 路径被手动改坏了可以用restoredefaultpath; rehash toolboxcache;执行完后再试一次。我遇到过这种问题用户之前为了调某个第三方工具箱手工删了部分默认路径导致globaloptim目录丢失。恢复默认路径几乎都能解决。6.2 GUI 的 Options 里找不到模拟退火相关项的补充说明老版optimtool里面模拟退火的参数面板很丰富可以逐个调整初始温度、衰减方式、扰动方式、最大迭代数。新版 GUI 由于没把模拟退火放进去所以你去 UI 上找这些参数栏也是找不到的。替代方案是使用命令行方式查看和修改全部选项% 显示所有可用选项 options optimoptions(simulannealbnd); display(options)或者用optimoptions配合点号访问来调节options optimoptions(simulannealbnd); options.InitialTemperature 200; options.MaxIterations 2000; options.TemperatureFcn temperaturelog;这个optimoptions对象会跟着函数传递效果和 GUI 里设置一模一样。值得注意的是新版 GUI 里就算你通过setSolver强行指定simulannealbnd界面右侧也不会弹出对应的参数设置面板这更进一步说明官方没有为它在界面上预留配置入口。6.3 模拟退火不收敛怎么办调节套路清单碰到模拟退火结果差、收敛慢、跳不出局部极小的情况我按经验列一个排查顺序先看exitflag。如果返回1说明正常收敛如果返回2或3说明达到MaxIterations或MaxFunctionEvaluations限制你需要增加代数或者减少问题复杂度。检查InitialTemperature是否太小。如果目标函数的波动量级在 1e6初始温度 100 几乎等于没有搜索能力温度很容易降到接近 0完全丧失接受差解的能力搜索会退化成纯贪心。把初始温度调到 1e7 或 1e8 去试。检查TemperatureFcn是否太激进。如果问题复杂直接把默认的temperatureexp换成temperaturelog降温慢一点搜索空间覆盖率显著提升。如果目标函数里存在大量平坦区域或离散变量检查AnnealingFcn的扰动范围是否匹配变量尺度。变量量级跨度大比如一个是 1e-5一个是 1e5需要做变量归一化或自定义AnnealingFcn。终极方案把ReannealInterval调小比如 20~30让算法频繁重升温可以强行打破局部吸引盆。代价是迭代次数增加但很多时候这是最简单的办法。6.4 混合整数问题中常见的约束表达勘误模拟退火对非线性约束的支持不像直接求解那样显式。simulannealbnd官方只支持边界约束lb和ub如果你的问题带一般非线性约束比如x(1) x(2) 1直接用会有问题。常规做法是使用惩罚函数法把违反约束的惩罚项加到目标函数里。在工程评估里这种办法非常有效function fval penalized_obj(x) fval original_objective(x) constraint_violation(x) * 1e6; end惩罚系数选多大有讲究。太小约束根本守不住太大又会造成悬崖式目标面干扰退火扰动。我习惯用“初始目标量级的 100 倍”作为惩罚系数然后再根据结果调整。退火因为能接受坏解反而对惩罚函数的系数没那么敏感容错性比 fmincon 强不少。这一点也是模拟退火 GUI 列表里消失的一个隐性原因它能解决的问题域很广但你必须自己处理一堆非线性约束的状态GUI 很难覆盖“约束 参数 算法”的全部排列组合。7. 结束前再说点实在的折腾了一圈我的体会是新版 MATLAB 的 GUI 越来越强但和“算法广度”并不总是正相关。模拟退火这类经典算法虽然没有在新版 Optimization App 中占一个坑但命令行支持和全局优化工具箱始终没落下它。对于做实际项目的朋友我建议把命令行当作主力GUI 只用来做快速模型验证。看到求解器列表里没有模拟退火时不要怀疑自己的工具箱而是回想一下这是官方在新版界面上的一次“悄然搬家”算法本体都还在toolbox\globaloptim里等你调用。如果这篇文章帮你省下了到处查文档的时间那这个坑也算没有白踩。