如果你正在用 MATLAB 做科研或工程项目,遇到“优化问题”几乎是必然的,比如参数标定、资源调度、路径规划、投资组合、模型拟合。很多人的第一反应是写一个 for 循环暴力遍历,或者直接在网上下载一段 fminsearch 代码改一改。这种做法在小规模、单变量场景下勉强能跑,可一旦约束条件变成十几个,变量变成高维,暴力搜索就完全不可行了。
这就是 MATLAB 优化算法工具箱(Optimization Toolbox)真正值得你花时间掌握的原因。
先说一个我的判断:学习优化工具箱,最重要的不是背下每个求解器的函数签名,而是学会两件事。第一,判断你面对的问题属于什么类型——线性还是非线性、有无约束、变量是否连续、是否要求整数;第二,理解 MATLAB 提供的 solver-based 和 problem-based 两种建模方式,前者是传统写法,后者更像是在写数学公式。搞清楚这两点,你基本就掌握了这个工具箱的使用逻辑。
这篇文章会从问题类型讲起,带你认识求解器分类和核心概念,再通过线性规划、混合整数线性规划、非线性约束优化几个完整示例,把代码跑通,最后专门讲清楚优化工具箱和遗传算法工具箱的区别与配合方式。内容密度不低,但每一步都是可以直接上手的。
1. 这篇文章真正要解决的问题
很多初学者拿到优化工具箱,打开文档会被吓到。函数太多:linprog、intlinprog、quadprog、fmincon、fminunc、fminsearch、lsqcurvefit、fsolve……每个还有一堆参数和选项,根本不知道从哪开始。
更大的痛点在于:不知道自己面对的问题属于哪一类,于是随便选一个求解器。比如明明是线性规划问题,却用了 fmincon,结果收敛慢、精度差;变量本应该是整数,却用 linprog 求解,求出来的解在生产实践里根本无法落地;最典型的是非线性问题只跑一次,陷入局部最优而不自知,最后得出的结论完全没有参考价值。
这篇文章要解决的核心问题,不是带你逐个研究几十个函数,而是帮你建立一条清晰的路径:
- 先判断问题类型;
- 再选择对应的求解器;
- 使用合适的建模方式把问题表达出来;
- 最后知道如何验证结果是否可信。
我还会说明优化工具箱和遗传算法工具箱(ga)各自的边界。很多人以为 ga 是优化工具箱里的一个函数,其实不是,它属于单独的全局优化工具箱(Global Optimization Toolbox)。它们解决的问题有重叠,但机制、能力和适用场景差异很大,结合起来用常常效果更好。
如果你是刚接触 MATLAB 优化的学生,或者正在做需要参数标定、曲线拟合、调度安排的开发者,这篇文章能帮你少走大量弯路。
2. 优化工具箱到底能做什么:问题类型与求解器总览
优化问题的标准描述是:在满足某些约束条件的前提下,寻找决策变量的取值,使目标函数最小化或最大化。这个描述听起来简单,实际落地时约束和目标函数的数学性质决定了求解难度。
按照性质,可以把常见优化问题分为几类:
- 线性规划:目标函数和约束都是线性的,一个典型例子是运输成本最小化。
- 混合整数线性规划:在线性规划基础上,部分变量必须取整数,比如设备选型中的 0-1 变量。
- 二次规划:目标函数包含二次项,例如带惩罚项的最小二乘问题。
- 非线性规划:目标函数或约束是非线性的,这是工程中最常见的类型。
- 数据拟合/最小二乘:给定数据点,找到一条曲线使误差平方和最小。
- 非线性方程组求解:本质上不是优化问题,但工具箱也会一并提供。
MATLAB 优化工具箱为这些类型都提供了对应的求解器:
| 求解器 | 适用问题 | 特点 |
|---|---|---|
| linprog | 线性规划 | 速度快,适合大规模线性问题 |
| intlinprog | 混合整数线性规划 | 支持整数和 0-1 变量 |
| quadprog | 二次规划 | 目标函数含有二次项 |
| fmincon | 有约束非线性优化 | 使用频率最高的求解器之一 |
| fminunc | 无约束非线性优化 | 要求目标函数连续可微 |
| fminsearch | 无导数无约束优化 | 适合不可导或导数难求的问题 |
| lsqcurvefit | 最小二乘曲线拟合 | 适合实验数据拟合 |
| lsqnonlin | 非线性最小二乘 | 更一般的最小二乘形式 |
| fsolve | 非线性方程组求解 | 求方程组根,不是真正意义上的优化 |
从使用频率看,fmincon 是重中之重,因为它能处理带非线性约束的最优化问题,覆盖了工程中的大多数场景。但我提醒一句:如果问题确定是线性的,就优先用 linprog,不要贪图 fmincon 的通用性。线性求解器在计算速度和结果稳定性上都有明显优势。
优化工具箱之外,还有一个经常被混淆的全局优化工具箱,里面包含遗传算法 ga 和粒子群算法 particleswarm。它们不依赖初始点,在非凸、多峰问题上能做出更好的全局搜索,但计算成本通常更高。后面我会专门讲它们的配合方式。
3. 三种你必须先搞懂的核心概念
3.1 决策变量、目标函数与约束
直观理解优化问题,可以拿“厨房配菜”来类比。
决策变量是你可以扭动的旋钮,比如每道菜做多少份、每种原料买多少斤。目标函数是你的评分标准,比如总利润或者总成本。约束条件是游戏规则,比如冰箱容量有限、每种原料不能超过库存、时间不能超过某个小时数。优化求解器做的,就是在规则允许的范围内找到那一组旋钮位置,让你的评分最高或最低。
在 MATLAB 中,决策变量通常用变量名 x 表示,可以是标量、向量或者矩阵。目标函数是输入 x、输出一个标量的函数。约束条件则分为边界约束、线性不等式约束、线性等式约束和非线性约束。它们在实际代码里的表达方式完全不同,这是初学者最容易出错的地方。
3.2 solver-based 与 problem-based
这是 MATLAB 优化工具箱里最需要理解的概念,也是决定你写代码风格的关键。
solver-based 方式是传统写法。你需要把目标函数写成普通函数句柄,把约束拆成矩阵 A、向量 b 等标准形式,然后传给求解器。它灵活、性能好,但要求你手工处理变量维度和约束排列。
problem-based 方式是 R2017b 之后主推的建模方式。你可以用 optimvar 声明优化变量,直接用数学表达式的形式写目标函数和约束,最后用 solve 求解。它看起来几乎就是把数学公式搬到代码里,可读性极高,非常适合快速验证模型。
两者的选择没有绝对优劣。我的建议是:新项目先考虑 problem-based,因为它能显著降低建模出错率;一旦遇到需要自定义回调函数、复杂梯度、大规模问题的场景,再切回 solver-based。
3.3 局部最优与初始点
非线性求解器大多采用迭代法,从某个初始点出发,沿着下降方向逐步逼近最优解。这意味着初始点的选择直接决定了最终结果。
如果目标函数只有一个“坑”,那从哪开始最终都会掉进同一个坑底,这是凸问题的特性。但如果目标函数有多个“坑”,不同初始值可能掉进不同的坑,最后只会返回局部最优,而不是全局最优。
这就解释了为什么求解这类问题不能只跑一次,而是要采用多初始点比较,或者干脆引入遗传算法这类全局优化方法。后面我会给出具体示例。
4. 环境准备与快速开始
使用优化工具箱之前,先确认你的 MATLAB 环境具备相关许可证。两个最常用的检查命令如下:
% 检查优化工具箱是否已安装,会显示版本信息 ver('optim') % 检查许可证是否可用,返回 1 表示可用,0 表示不可用 license('test', 'Optimization_Toolbox')如果 ver 输出里没有 Optimization Toolbox,或者 license 返回 0,说明当前环境没有安装或激活对应工具箱。安装和激活属于 MATLAB 授权管理的操作,请使用官方渠道完成。
进一步确认求解器文件是否可见:
which linprog which fmincon正常输出会显示对应函数的完整路径。如果提示 not found,说明工具箱未被正确加入路径。
版本方面,本文给出的代码基于 Optimization Toolbox 近几个主要版本的通用 API。不同版本之间可能出现细微差异,比如旧版本使用 optimset 管理选项,新版推荐使用 optimoptions。建议以你本机的帮助文档为准,本文重点是通用思路。
命令行快速做一个最小验证:
% 一个简单的线性规划最小二乘测试 f = [1; 1]; A = [1, 1]; b = [1]; lb = [0; 0]; x = linprog(f, A, b, [], [], lb);如果这段代码能顺利返回 x,说明你的优化工具箱环境基本可用。
5. 完整示例代码实现
下面从易到难给出 4 个可运行的完整示例。每个示例都附带问题背景、代码、运行说明和结果解释,你可以直接复制到自己的脚本里修改使用。
5.1 示例 1:linprog 求解线性规划
场景:一家工厂生产两种产品,产品 A 每件利润 4 千元,产品 B 每件利润 3 千元。两种产品都需要消耗原材料 M 和 N,约束条件如下:
- 每件 A 消耗 M 原料 2 单位,每件 B 消耗 M 原料 1 单位,M 总库存 10 单位;
- 每件 A 消耗 N 原料 1 单位,每件 B 消耗 N 原料 1 单位,N 总库存 8 单位。
现在要确定两种产品各生产多少件,才能让总利润最大。
linprog 默认求解最小值,所以最大化目标函数时,需要把系数取负。完整代码如下:
% 文件:example_linprog.m % linprog 求解最大利润问题 % 目标:max profit = 4*x1 + 3*x2 % 约束: % 2*x1 + x2 <= 10 % x1 + x2 <= 8 % x1 >= 0, x2 >= 0 f = [-4; -3]; % 目标函数系数,注意取负 A = [2, 1; 1, 1]; % 线性不等式约束系数矩阵 b = [10; 8]; % 约束右侧常数 lb = [0; 0]; % 变量下界 [x, fval, exitflag, output] = linprog(f, A, b, [], [], lb); fprintf('最优产量:x1=%.2f, x2=%.2f\n', x(1), x(2)); fprintf('最大利润:%.2f\n', -fval); fprintf('退出标志:%d\n', exitflag); fprintf('迭代次数:%d\n', output.iterations);关键逻辑拆解:
- 目标函数系数 f 取负,是因为 linprog 求最小值,而原始问题是最大化利润。
- A 和 b 把两条约束写成矩阵形式
A*x <= b。 - lb 指定两个变量的下界为 0,生产数量不能是负数。
- 最终输出里 -fval 才对应原始最大利润值,因为 fval 是负后的最小值。
运行后,预期结果是 x1=2、x2=6,最大利润 26。这个结果可以在纸上手算验证,用于确认求解器输出正确。
5.2 示例 2:intlinprog 求解混合整数线性规划
场景:同样是生产计划,但这次产品数量必须为整数,因为产品以“件”为单位,无法生产半件。
linprog 求出的可能是小数解,但 intlinprog 专门处理这种整数要求。代码如下:
% 文件:example_intlinprog.m % intlinprog 求解整数生产计划 % 目标:max profit = x1 + 2*x2 % 约束: % 2*x1 + 4*x2 <= 16 % x1 + x2 <= 8 % x1, x2 为非负整数 f = [-1; -2]; intcon = [1; 2]; % 要求 x1 和 x2 都取整数 A = [2, 4; 1, 1]; b = [16; 8]; lb = [0; 0]; [x, fval, exitflag] = intlinprog(f, intcon, A, b, [], [], lb); fprintf('整数最优解:x1=%d, x2=%d\n', x(1), x(2)); fprintf('最大利润:%.2f\n', -fval); fprintf('退出标志:%d\n', exitflag);与 linprog 相比,这里多了一个关键参数 intcon,它是需要取整的变量索引数组。如果你的模型中只有 x2 要求整数,那么 intcon 就是 2。
intlinprog 的底层算法是分支定界,时间复杂度随整数变量规模增长较快,所以实际项目中如果整数变量很多,需要合理设置求解时间和节点上限。
5.3 示例 3:fmincon 求解带非线性约束的优化
场景:设计一个二维参数优化问题,目标函数是二次函数,同时包含一个圆形区域约束和一个线性约束。
问题定义:
- 最小化目标函数:f(x) = x1^2 + x2^2 + 4x1 + 6x2;
- 圆形约束:x1^2 + x2^2 <= 25;
- 线性约束:x1 + x2 >= 2;
- 边界约束:x1, x2 均在 0 到 10 之间。
fmincon 的约束格式要求所有不等式都写成A*x <= b和c(x) <= 0的形式。所以线性约束 x1 + x2 >= 2 要变换为 -x1 - x2 <= -2,对应的 A 取 [-1, -1],b 取 -2。非线性约束则通过 nonlcon 函数返回两个值:不等式约束 c(x) 和等式约束 ceq(x)。
% 文件:example_fmincon.m % fmincon 求解带非线性约束的最小化问题 % 目标函数 fun = @(x) x(1)^2 + x(2)^2 + 4*x(1) + 6*x(2); % 初始点 x0 = [1; 1]; % 线性不等式约束 A*x <= b A = [-1, -1]; b = [-2]; % 线性等式约束(无) Aeq = []; beq = []; % 边界约束 lb = [0; 0]; ub = [10; 10]; % 非线性约束:c(x) <= 0 且 ceq(x) = 0 nonlcon = @(x) deal(x(1)^2 + x(2)^2 - 25, []); [x, fval, exitflag, output] = fmincon(fun, x0, A, b, Aeq, beq, lb, ub, nonlcon); fprintf('最优解:x1=%.4f, x2=%.4f\n', x(1), x(2)); fprintf('最优目标值:%.4f\n', fval); fprintf('退出标志:%d\n', exitflag); fprintf('约束违反量:%.6f\n', output.constrviolation);这里最需要注意的是 nonlcon 函数。deal 的作用是同时返回两个输出参数,第一个是非线性不等式约束,第二个是非线性等式约束。如果等式约束不存在,就写成 []。
fmincon 的默认算法会根据问题自动选择,也可以使用 optimoptions 手动指定。实际项目中我建议至少明确设置一次选项,便于控制迭代过程,后面会演示。
5.4 示例 4:problem-based 声明式建模
前面三个示例都是传统的 solver-based 写法。现在用 problem-based 方式重写示例 2 的整数规划问题,展示建模方式的差异。
% 文件:example_problembased.m % problem-based 建模方式 prob = optimproblem('ObjectiveSense', 'maximize'); % 声明优化变量:两个非负整数变量 x = optimvar('x', 2, 'Type', 'integer', 'LowerBound', 0); % 目标函数直接用数学表达式 prob.Objective = x(1) + 2*x(2); % 约束直接写成不等式 prob.Constraints.cons1 = 2*x(1) + 4*x(2) <= 16; prob.Constraints.cons2 = x(1) + x(2) <= 8; % 求解 sol = solve(prob); fprintf('整数最优解:x1=%d, x2=%d\n', sol.x(1), sol.x(2)); fprintf('最大利润:%.2f\n', prob.Objective(sol.x));这段代码在可读性上明显优于 solver-based 版本。optimvar 声明出两个整数变量,目标函数和约束写法和数学公式几乎一样,不需要手工拆解矩阵。
problem-based 建模会通过 prob2struct 自动转换为底层求解器需要的标准形式。对于初学者和模型验证阶段,这种表达方式能大幅减少因维度处理和符号方向导致的低级错误。
6. 运行结果与效果验证
每次求解完成后,除了看目标值,还要做系统性检查。没有验证的优化结果不能直接使用,这是工程上的底线。
6.1 exitflag 代表什么
依次运行上面的示例,exitflag 是一个非常重要的诊断信号。
- exitflag = 1,表示求解正常收敛到某个局部最优;
- exitflag = 0,表示达到最大迭代次数或最大函数评价次数,尚未收敛;
- exitflag = -2,表示约束不可行,找不到满足所有约束的可行域;
- exitflag = -3,表示问题无界,目标函数可以无限减小或增大。
如果 exitflag 不为 1,第一步应该查看返回值 output.message,这里会给出更具体的解释。
6.2 检查约束是否真正满足
很多初学者只看目标值变小就认为成功了,这是危险的。例如 fmincon 在迭代结束时可能返回一个目标值还不错、但约束被轻微违反的解。
使用 output.constrviolation 可以直观判断:
% 在 fmincon 返回后检查约束违反量 fprintf('约束违反量:%.6f\n', output.constrviolation);这个值应该非常小,一般在 1e-6 量级。如果 violation 明显偏大,说明约束处理有问题,需要回头检查 nonlcon 的函数签名和符号。
6.3 使用 PlotFcn 观察迭代过程
为了更直观地确认求解过程,可以给求解器设置显示选项和绘图回调:
% 文件:example_optimoptions.m % 使用 optimoptions 控制 fmincon 迭代输出和绘图 options = optimoptions('fmincon', ... 'Display', 'iter', ... 'PlotFcn', 'optimplotfval', ... 'Algorithm', 'sqp'); % 沿用示例 3 的目标函数、约束和初始点 fun = @(x) x(1)^2 + x(2)^2 + 4*x(1) + 6*x(2); A = [-1, -1]; b = [-2]; lb = [0; 0]; ub = [10; 10]; nonlcon = @(x) deal(x(1)^2 + x(2)^2 - 25, []); x0 = [1; 1]; [x, fval, exitflag] = fmincon(fun, x0, A, b, [], [], lb, ub, nonlcon, options);Display 设置为 iter 后,命令行会逐次打印迭代信息;optimplotfval 会画出每轮迭代中目标函数值的变化曲线。看到曲线单调下降并在后期趋于平坦,通常说明收敛过程是正常的。
6.4 多初始点比较
对于非线性问题,单次求解不够。一个简单有效的做法是生成多个随机初始点,逐个求解后比较结果:
% 多初始点敏感性分析 rng('default'); best_fval = Inf; best_x = []; for i = 1:10 x0_try = lb + rand(2,1) .* (ub - lb); [x_try, fval_try, exitflag] = fmincon(fun, x0_try, A, b, [], [], lb, ub, nonlcon); if exitflag > 0 && fval_try < best_fval best_fval = fval_try; best_x = x_try; end end fprintf('多次求解得到的最优目标值:%.4f\n', best_fval);如果多次求解结果差异较大,说明问题存在多个局部极值,应该考虑使用遗传算法等全局优化方法。
7. 遗传算法工具箱与优化工具箱如何配合
很多资料会把优化算法工具箱和遗传算法工具箱放在一起讲解,因为它俩解决的问题域高度重叠,但求解机制完全不同。
优化工具箱里的 fmincon、fminunc、linprog 等求解器,本质上都是基于梯度或线性代数的方法,迭代速度快、收敛精度高,但容易陷入局部最优。遗传算法 ga 则模拟生物进化过程,通过种群选择、交叉、变异不断搜索解空间,天然具备跳出局部极值的能力,但不擅长度数收敛。换句话说,ga 擅长“找到大概位置”,fmincon 擅长“在附近精确落点”。
正确的使用方式,是把两者串联起来。我推荐两种模式。
7.1 模式一:使用 ga 的 HybridFcn 自动精修
ga 本身就提供了混合求解选项,可以指定一个局部求解器在遗传迭代收尾时做精修:
% 文件:example_ga_hybrid.m % 使用 ga 时自动调用 fmincon 作为混合精修器 options = optimoptions('ga', ... 'HybridFcn', 'fmincon', ... 'Display', 'iter', ... 'PlotFcn', 'gaplotbestf'); nvars = 2; fun = @(x) x(1)^2 + x(2)^2 + 4*x(1) + 6*x(2); lb = [0; 0]; ub = [10; 10]; nonlcon = @(x) deal(x(1)^2 + x(2)^2 - 25, []); [x_ga, fval_ga] = ga(fun, nvars, [], [], [], [], lb, ub, nonlcon, options); fprintf('ga 搜索加 fmincon 精修结果:x1=%.4f, x2=%.4f, fval=%.4f\n', ... x_ga(1), x_ga(2), fval_ga);注意,ga 返回的 x 是行向量,而 fmincon 习惯用列向量。如果后续需要把 ga 的结果传给 fmincon,需要做一次转置。
7.2 模式二:手动两阶段求解
HybridFcn 是一步到位的自动模式,但如果你想在两个阶段之间加入更多控制逻辑,可以手动串联:
% 文件:example_ga_fmincon.m % 手动两阶段:先全局搜索,再局部精修 fun = @(x) x(1)^2 + x(2)^2 + 4*x(1) + 6*x(2); nvars = 2; lb = [0; 0]; ub = [10; 10]; nonlcon = @(x) deal(x(1)^2 + x(2)^2 - 25, []); % 第一阶段:全局粗搜索 x_global = ga(fun, nvars, [], [], [], [], lb, ub, nonlcon); % 第二阶段:以 ga 结果为初始点,做局部精修 A = [-1, -1]; b = [-2]; x0_local = x_global(:); % 转成列向量 [x_local, fval_local] = fmincon(fun, x0_local, A, b, [], [], lb, ub, nonlcon); fprintf('全局搜索结果:x1=%.4f, x2=%.4f\n', x_global(1), x_global(2)); fprintf('局部精修结果:x1=%.4f, x2=%.4f, fval=%.4f\n', ... x_local(1), x_local(2), fval_local);这种做法的价值在于:先利用 ga 在全局范围内找到有希望的“盆地”,再由 fmincon 在盆地里走到底,既避免了局部最优,又保证了最终收敛精度。
7.3 什么情况下不需要 ga
如果你确定问题是凸的、单峰的,或者目标函数是线性的,就没有必要动用遗传算法。它每次运行要维护一个种群,计算成本远高于梯度法,在凸问题上纯属浪费算力。合理的选择顺序是:先判断问题结构,再用最便宜的求解器。
8. 常见问题与排查思路
以下是实际使用中高频出现的问题,我整理成了排查表,方便你检索定位。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 求解器报错 Objective function is undefined at initial point | 初始点不在可行域,或目标函数在初始点处返回 NaN | 在命令行单独计算 fun(x0),检查输出 | 调整初始点,或在目标函数内部加数值保护 |
| exitflag = 0,结果不收敛 | 迭代次数或函数评价次数不足 | 查看 output.message 和迭代曲线 | 增大 MaxIterations、MaxFunctionEvaluations |
| 约束被明显违反 | 非线性约束 c(x) 符号写反 | 打印 nonlcon 在解处的值 | 确认返回格式是 c(x) <= 0 和 ceq(x) = 0 |
| 不同初始点得到不同结果 | 问题非凸,存在多个局部极值 | 多初始点比较,或查看目标函数峰谷分布 | 改用 ga、particleswarm 等全局优化方法 |
| fmincon 报维度不一致错误 | A 的列数与决策变量数不一致 | 用 size(A) 和变量数比对 | 保证约束矩阵维度正确 |
| 旧代码报错 optimset 无法使用 | 新版 MATLAB 逐步淘汰旧选项 API | 查看错误中给出的替代提示 | 改用 optimoptions |
| 整数变量被当成连续变量 | 用了 linprog 或 fmincon 而不是 intlinprog | 检查问题定义,确认整数要求 | 改用 intlinprog,并正确设置 intcon |
| ga 运行非常慢 | 种群规模、最大代数设置过大 | 观察每一代耗时和收敛速度 | 减小 PopulationSize,或先做变量筛选 |
排查时一般遵循“由外到内”的顺序:先确认问题类型,再检查初始点和约束表达,再看求解器反馈的诊断信息,最后才是调整算法参数。
9. 最佳实践与工程建议
到这里,工具箱的基本使用流程已经完整跑通了。最后补充几条我在实际项目中沉淀下来的工程建议,供你在真实场景中参考。
9.1 先分类,再选求解器
这是最重要的原则。拿到问题,先问自己:目标函数是否线性?约束是否线性?变量是否要求整数?是否存在非线性约束?只有把这些问题回答清楚,才能从求解器表格里选出正确的函数。直接搬一个 fmincon 模板到所有场景,是很常见的初级错误。
9.2 用 optimoptions 统一管理参数,不要全塞在函数签名里
几个求解器共享的选项,比如 Display、PlotFcn、MaxIterations、ConstraintTolerance,都应该集中放在 optimoptions 里。这样既方便阅读,也便于后期调整参数做对比实验。
9.3 正式求解前,在初始点手动验证目标函数和约束函数
很多求解器报错,根源就是目标函数在初始点处无法计算,对数和除法运算出现 NaN,或者非线性约束函数有语法错误。最有效的预防办法是,在命令行手动调用一次:
% 人工验证函数在初始点能否正常计算 fun(x0) nonlcon(x0)看到真实输出,能避免大量低级错误。
9.4 变量缩放与归一化
当决策变量的数量级差异很大时,比如一个变量在 0.001 量级、另一个在 10000 量级,梯度和收敛性都会变差。一个可行的做法是把变量归一化到相近范围,求解后再映射回去。这个操作对 fmincon 尤其重要。
9.5 能提供解析梯度,就尽量提供
fmincon 允许通过 SpecifyObjectiveGradient 选项传入解析梯度。对高维问题,这能显著减少迭代次数,并提高最终精度。如果解析梯度不好求,网格搜索求解只能作为备选方案。
9.6 不要只保存解,要保存诊断信息
工程项目的复盘往往需要回溯求解当天的设置和结果。建议把 x、fval、exitflag、output、options 一起保存到结构体里。这样出问题或者要复现结果时,不需要重新跑一遍。
9.7 大规模问题考虑并行
全局优化工具箱中的某些方法支持并行计算,可以通过 optimoptions 设置 UseParallel 为 true。但要注意,并行存在通信开销,小规模问题开了并行可能更慢,先比较串行和并行的时间再决定。
9.8 遵循最小可行域验证原则
如果你把优化算法嵌入到生产系统中,每次调整模型或约束,都应该先在简化数据集上验证可行域和结果。对生产系统来说,一个不可行的约束条件比求解稍微慢一点严重得多。
读完这篇文章,你已经掌握了优化算法工具箱的基本框架:如何判断问题类型、如何选择求解器、两种建模方式怎么用、如何验证结果,以及遗传算法和局部优化器如何配合。建议下一步打开 MATLAB 帮助文档,浏览 Optimization Toolbox 的官方示例,把本文的 4 个示例改成你自己的数据跑一遍。特别是 fmincon 和 problem-based 建模,工程量不算大,但会直接影响你后续所有优化任务的效率和判断精度。