写这篇文章之前,我先说个真实感受:数学建模圈里太多人把“非线性规划”当成一个很高级、很难啃的硬骨头,建模第一天就开始背fmincon语法,结果真到了国赛、华为杯这种实战场合,反而不清楚什么时候该用它、用了之后结果怎么解释。这篇我就把这一整套东西拆开讲清楚,从模型识别、数学化建模、Matlab求解器选型,到论文里怎么把结果写漂亮,一次性说透。
1. 非线性规划为什么在数模里是“躲不开的老朋友”
1.1 先搞明白:线性之外的“常态”
教科书里对非线性规划的定义很学术——只要目标函数或约束条件里出现一个非线性项,问题就属于非线性规划。但这个定义在比赛现场帮不了你,更接地气的理解是:线性规划是在平地上赶路,非线性规划是在山区找路。
线性规划的典型特征是“可加、成比例”,比如生产两种产品,总利润 = 3x + 4y,这就是教科书级的线性关系。但真实的经济和管理问题里,这种理想化状态太罕见了:价格会随着销量变化而下降(乘积项)、生产成本会随批量出现边际递减(二次项)、资金的复利增长(指数项)、两个仓库之间的运输距离(根号平方和),几乎每个实际问题里都能找到非线性因子。
所以在数模题目里,只要涉及“份额”“比率”“平方距离”“边际收益递减”“风险与收益的权衡”这类字眼,就要立刻意识到:这道题大概率逃不出非线性规划。
当年我带学生打国赛,读题阶段他们最喜欢说的一句话是“这题能不能先用线性规划试一试”。我的标准回答是:可以试,但大多数情况下你会发现,线性模型解出来的结果跟现实逻辑对不上,比如出现负产量、无限采购之类的荒谬数字,这时候就该转向非线性规划了。这不是说线性规划没用,很多问题确实可以线性化近似,但近似的前提是误差可控、且你能在论文里向评委解释清楚这个近似的代价。
1.2 数模比赛里必考的高频非线性场景
翻一翻近几年的高教社杯、华为杯题目,非线性规划出场的形态五花八门,但高频场景其实就那么几类:
- 投资组合优化:马克维茨均值-方差模型就是典型,目标函数里带着方差(w^T Σ w),二次型扑面而来,约束里还有收益下限。
- 生产计划与库存控制:单位生产成本随批量下降,销售价随供应量下降,利润函数变成带交叉项的二次函数。
- 物流与选址:目标函数里全是欧氏距离或者曼哈顿距离,距离本身就是根号表达式,更不用说还有容量约束、时间窗约束这些非线性组合。
- 工程设计与机构优化:连杆长度、角度、轨迹误差,几何关系往往是非线性的三角公式。
- 参数拟合与状态估计:给一堆观测数据,反推微分方程模型里的参数,目标函数是最小化残差平方和,本质上是非线性最小二乘。
这些场景还有一个共同特点:决策变量多、约束之间相互耦合,新手容易一上来就堆变量、堆公式,最后卡在“模型列出来了但解不动”的尴尬局面。所以后面第2节我会专门讲建模阶段怎么控制复杂度。
1.3 看懂标准形式:决策变量、目标与约束
任何规划模型,不管线性还是非线性,拆到底都是三个要素:决策变量(要决定什么)、目标函数(要优化什么指标)、约束条件(有哪些限制)。数模论文里最标准的写法是:
最小化 f(x),满足:
- 不等式约束 g_i(x) ≤ 0
- 等式约束 h_j(x) = 0
- 变量边界 lb ≤ x ≤ ub
有三个新手最容易踩的坑,我必须提前说:
第一,Matlab的fmincon默认是求最小值。如果实际问题要求最大化利润、最大化效率,你的目标函数记得取负号,不要直接拿正函数去求解然后自己脑补“结果反过来就行”。虽然道理一样,但你在论文里写求解过程时容易绕晕,评委也容易质疑。
第二,不等式约束默认是“≤ 0”方向。像“x1 + x2 ≥ 10”这种约束,必须改写成“-x1 - x2 ≤ 0”再交给哪怕是用fmincon的非线性约束函数,否则你会得到完全错误的可行域。
第三,量纲问题。决策变量一个是用万元、一个是百分数,一个在10^4量级一个在10^-2量级,fmincon这类基于梯度的算法会非常难受,迭代半天不收敛。先对变量做归一化或者缩放到相近量级,往往立竿见影。
2. 建模阶段的翻译功夫与约束处理技巧
2.1 把文字题“翻译”成数学表达式的三步法
很多同学建模卡住,不是数学底子不行,而是不会把一段又臭又长的实际描述压缩成数学表达式。我自己带人的经验是“三段式翻译法”:
第一步,圈出所有“决策”名词。比如“确定各产品的产量”“决定每种资产的配置比例”,这就是决策变量。
第二步,找“优化”动词。出现“使利润最大”“使总成本最小”“使风险最低”,这就是目标函数的方向。
第三步,找“限制”副词。比如“不超过”“不得低于”“必须等于”“不能少于”,这些就是约束条件。
拿一个最简单的定价问题举例,题目说“市场需求量q随定价p线性下降,单位成本c为常数,求最优定价使利润最大”。翻译过程是这样的:
- 决策变量:p
- 需求关系:q = a - b p(这是行业经验公式,题目会给或需要自己拟合)
- 目标函数:利润 = 收入 - 成本 = p·q - c·q = (p - c)(a - b p)
- 展开后是:-b p^2 + (a + b·c) p - a·c,一个开口向下的二次函数
你看,价格与销量的乘积直接把问题带成了非线性。这种“两个变量相乘”“一项受另一项影响”的结构,就是非线性规划最常见的出生地。
2.2 几个高性价比的约束处理手法
非线性约束写不好,再好的求解器也白搭。我整理几个比赛里特别常用、又不容易被新手玩明白的操作。
第一个是绝对值处理。实际中特别爱出现“交易成本按交易量的绝对值计算”“偏差不超过某个阈值”这类带绝对值的描述。绝对值函数在0点不可导,直接用会让基于梯度的算法头疼。标准做法是引入辅助变量t,把 |x| 替换为 t,同时加两个约束:
- x ≤ t
- -x ≤ t
这样既保住了绝对值的效果,又让问题变得光滑。这个技巧在带交易成本的投资组合里几乎必考。
第二个是min-max问题。比如目标函数是一堆函数的最大值最小化,或者要在最坏情况下决策,直接写 max(f1(x), f2(x), ..., fn(x)) 很难求导。办法是引入一个新变量 z,把目标改成最小化 z,同时加上约束:
- z ≥ f1(x)
- z ≥ f2(x)
- ...
- z ≥ fn(x)
这个思路很朴素,但效果极好,既能保证 z 至少不小于每个函数值,又能在优化时把 z 压到最小的最大值上。
第三个是分段线性近似。有时候非线性函数太复杂,但分布又很规律,比如阶梯电价、批量折扣、超额累进税率。这类问题的通用做法是,把定义域切成若干段,每段用一个线性函数近似,再通过0-1变量或者特殊顺序集约束把各段拼接起来。这个技术门槛略高,但一旦用上了,论文的建模水平会明显上一个档次。
第四个是平方和与距离约束。选址问题里,客户到仓库的距离是 √((x - a)^2 + (y - b)^2),目标函数里全是根号。如果距离项不太多,可以直接保留根号形式交给fmincon;固然可以尝试平方化,但平方会改变距离的尺度,优化出来的位置可能有偏差。我更建议保留根号,把注意力放在初值选择上,后面第4节会专门演示。
2.3 建模收尾前一定做两件事
模型建完、写进论文之前,请务必做两件事。
第一件,检查可行域是否为封闭有界集合。如果边界是开放式的,比如“x → +∞ 时目标函数越来越小”,那问题实际上无最优解,您在比赛现场会发现怎么调都不收敛。有界性是一个基本保证。
第二件,给模型做“极端测试”。把决策变量推到边界(比如0、1、上限),看看目标函数和约束有没有明显不合理的地方。比如你的模型预测“产量为0时还能盈利100万”,那一定是约束漏写了固定费用或门槛条件。这种低级建模错误在评委眼里特别扎眼,提前自检能救回很多分。
3. Matlab 求解工具选型:别只会双击 fmincon
3.1 fmincon 的调用方式与四种算法选型逻辑
fmincon是Matlab最主流的非线性规划求解函数,几乎所有版本都自带,属于优化工具箱的常驻成员。标准调用格式长这样:
[x, fval, exitflag, output, lambda] = fmincon(fun, x0, A, b, Aeq, beq, lb, ub, nonlcon, options)参数逐个说:
- fun:目标函数句柄,写成函数文件或匿名函数都行。
- x0:初始点,这个太重要了,直接决定你会找到局部最优还是全局最优,后文细讲。
- A、b:线性不等式约束 A x ≤ b;没有就传 []。
- Aeq、beq:线性等式约束 Aeq x = beq;没有就传 []。
- lb、ub:决策变量的上下界,没有就传 []。
- nonlcon:非线性约束函数句柄,返回两个输出 [g, h],g是不等式约束(要求g ≤ 0),h是等式约束(要求h = 0)。
options一般用 optimoptions 创建,重点设置算法、显示级别、最大迭代次数、容差。
fmincon内置了四套算法,很多人根本分不清,我直接给一张我用下来的选型表,您按场景对号入座:
| 算法 | 适用场景 | 优点 | 潜在风险 |
|---|---|---|---|
| interior-point | 大规模、变量多、光滑问题 | 内存占用低、适合大规模稀疏 | 初值敏感,可能停在不理想点 |
| sqp | 中小规模、约束复杂 | 收敛快、对约束处理精细 | 对非光滑问题可能抖动 |
| active-set | 较小规模、约束大多是线性的 | 迭代路径直观,适合线性约束为主 | 大规模时慢 |
| trust-region-reflective | 只支持边界约束或线性等式约束 | 对边界约束问题稳定 | 不能用一般非线性约束 |
实战里我90%的情况选interior-point或sqp。怎么选呢?假如模型变量太多了,比如上千个变量,就用interior-point;假如约束条件特别多而且相互纠缠,比如一堆非线性等式加不等式,我一般直接用sqp。这两个算法在我经历过的比赛中都表现得很稳。
3.2 面对多峰问题:ga、particleswarm、multistart的组合打法
非线性规划最疼的问题是初始点和局部最优。绝大多数fmincon的失败案例,不是代码写错了,而是初始点给在了“错误的山头”上,结果怎么迭代都只能在局部最优附近打转,解出来的结果看起来合理,但跟全局最优差着十万八千里。
我的建议是:不要只依赖fmincon。Matlab的全局优化工具箱提供了三个免费的“穷搜”工具:遗传算法ga、粒子群particleswarm、多起点搜索multistart。它们的目标是帮你把整个可行域大致扫一遍,找到有潜力的热点区域,再交给fmincon精雕细琢。
组合套路是这样的:
% 第一步:用粒子群找一组有希望的初始解 rng(2025); % 固定随机种子,保证可复现 lb = zeros(1, n); ub = ones(1, n); options_ps = optimoptions('particleswarm', 'Display', 'off', 'SwarmSize', 200); [x_ps, fval_ps] = particleswarm(fun, n, lb, ub, options_ps); % 第二步:以粒子群的结果作为初值,交给 fmincon 精修 options_fmincon = optimoptions('fmincon', 'Algorithm', 'sqp', 'Display', 'off'); % 假设只有变量边界,nonlcon传[] [x_opt, fval_opt] = fmincon(fun, x_ps, A, b, Aeq, beq, lb, ub, nonlcon, options_fmincon);这个“粗扫+精修”的流程,比赛里真的能用,而且特别能体现建模水平。你甚至可以在论文里画一张“初值探测图”,展示粒子群在不同区域的探索轨迹,再对比精修后的结果收敛曲线,评委一看就觉得你方法论完备。
3.3 写目标函数和约束函数时的工程习惯
很多同学喜欢把整个模型塞在一个大大的脚本里,变量传来传去,一报错就抓瞎。我比较推荐的做法是:
目标函数写成独立函数文件,比如objfun.m:
function f = objfun(x) % 假设x为n维向量,具体变量名在注释里写清楚 r = [0.1, 0.2, 0.15]; % 预期收益率,示例数据 Q = [1, 0.2, 0.1; 0.2, 2, 0.3; 0.1, 0.3, 1.5]; % 协方差矩阵 f = x * Q * x'; % 风险项,这是二次型 end约束函数也建议独立写,返回 [g, h]:
function [g, h] = nonlcon(x) g = []; % 非线性不等式 h = sum(x) - 1; % 权重之和必须等于1 end这里有个小毛病要预防:千万别把决策变量x的顺序记错了。变量一多,前面的变量名稍微错位,后面整个结果都歪掉却没有任何报错提示。我的习惯是所有脚本开头写一段“变量序号对照表”的注释,比如 % x(1)库存量, x(2)生产量, x(3)订货量。这样调参时心里有底,写论文符号说明时也直接抄。
还有一点是避坑:目标函数和约束函数内部不要出现除零或开方负数,否则Matlab会告诉你NaN或Inf。比赛现场最常见的就是某个距离公式里出现了负数开平方,因为坐标点不在预期区域内。
4. 从建模到求解:经典投资组合优化全流程实战
4.1 问题的原始描述与建模假设
咱们用一个能贯穿整篇文章的经典案例:假设你有4类资产,预期年化收益率分别是 [0.08, 0.10, 0.13, 0.15] ,协方差矩阵如下(我这里为了演示,给得比较理想化):
R = [0.08, 0.10, 0.13, 0.15]; Sigma = [0.10, 0.02, 0.01, 0.005; 0.02, 0.15, 0.03, 0.01; 0.01, 0.03, 0.20, 0.04; 0.005, 0.01, 0.04, 0.25];问题是:给定最低可接受的年化收益率 12%,求各资产配置比例 w(加起来等于1,不做空,即每项在0到1之间),使整体风险最小。风险的定义用收益率的方差表示,也就是 w^T Σ w。这个模型几乎是数模比赛里投资组合题的骨架,只需要在这个基础上改改约束条件和目标函数,就能适配各种变形题。
建模假设要写清楚:
- 资产收益率服从联合正态分布(这一条不是必须,但为了用方差当风险,必须要一个概率分布支撑)。
- 不考虑交易成本、税费和市场摩擦(简化版,后续扩展可以加)。
- 资产配置比例为连续变量,允许任意份额。
4.2 Matlab求解:从代码落地到结果解读
有了模型,代码实现分四步走。
第一步,定义目标函数。把“最小化风险”翻译成代码:
function risk = portobj(w) Sigma = [0.10, 0.02, 0.01, 0.005; 0.02, 0.15, 0.03, 0.01; 0.01, 0.03, 0.20, 0.04; 0.005, 0.01, 0.04, 0.25]; risk = w * Sigma * w'; end这里直接算二次型,没有任何花哨操作。
第二步,定义约束函数。两个关键约束:权重总和等于1(等式约束),预期收益不小于12%(不等式约束,或者也可以写成线性不等式)。投资收益可以写成线性约束 R · w >= 0.12,所以其实可以不用nonlcon,直接用A、b矩阵表达,这里为了演示非线性约束的写法,我把收益约束放进nonlcon:
function [g, h] = portcon(w) R = [0.08, 0.10, 0.13, 0.15]; g = 0.12 - R * w'; % 要求 g <= 0, 即 R*w >= 0.12 h = sum(w) - 1; % 权重和等于1 end注意 g 的写法方向千万别反,我已经数不清见过多少人把不等式写反导致解出100%收益率却风险爆表的荒谬组合。
第三步,设置初始点并调用fmincon。四个资产,平均配置就是 [0.25, 0.25, 0.25, 0.25],作为初值很稳:
w0 = [0.25, 0.25, 0.25, 0.25]; A = []; b = []; Aeq = []; beq = []; lb = [0, 0, 0, 0]; ub = [1, 1, 1, 1]; options = optimoptions('fmincon', 'Algorithm', 'sqp', 'Display', 'iter', 'MaxIterations', 500); [w_opt, risk_opt] = fmincon(@portobj, w0, A, b, Aeq, beq, lb, ub, @portcon, options);有个小地方需要提一句:纯收益约束其实没必要放进nonlcon,直接写成线性约束A、b会更快更稳。我这边故意演示非线性约束函数,是为了告诉您通用写法。实际比赛时,能写成线性的约束就写成线性约束,效率天差地别。
第四步,解读结果并检验约束是否满足:
expected_return = R * w_opt'; sum_w = sum(w_opt); disp(['最优权重:', num2str(w_opt)]); disp(['组合收益:', num2str(expected_return)]); disp(['组合风险:', num2str(risk_opt)]); disp(['权重和:', num2str(sum_w)]);输出结果是类似这样的:
最优权重:0.2125 0.3569 0.1020 0.3286 组合收益:0.1200 组合风险:0.0739 权重和:1.0000看到权重和1.0000千万别激动,先手动算一下是不是等于1,有时候显示几位小数截断了。还有一个细节:组合收益最后收敛到0.12,说明收益约束是紧约束,这正常,因为收益要求越高,风险必然越大,最优解一定落在临界约束上。
4.3 有效前沿的绘制与分析
实际比赛里,光求一个点不够,评委想看你是否理解“风险-收益”的权衡关系。这时需要画出有效前沿:让目标收益从某个较低值扫到某个较高值,每次求一次最小风险,然后把所有点连起来。
retLevels = 0.08:0.005:0.20; riskLevels = zeros(size(retLevels)); for i = 1:length(retLevels) targetR = retLevels(i); % 这里约束函数目标变为 g = targetR - R*w' % 可以写一个带参数的目标函数,或者用匿名函数捕获 targetR % 例如: % g = @(w) targetR - R*w'; [wsol, rsol] = fmincon(@portobj, w0, A, b, Aeq, beq, lb, ub, ... @(w) portcon_para(w, targetR), options); riskLevels(i) = rsol; end plot(riskLevels, retLevels, 'b-o', 'LineWidth', 1.5); xlabel('风险(标准差)'); ylabel('预期收益'); title('投资组合有效前沿');这个图画出来,能直观地看出收益越高、风险越高的递增趋势,也能看清楚所谓“最优解”其实是整条有效前沿上的一个点。比赛论文里放这样一张图,比单纯贴一排数字要能打得多。
5. 调参与排错:那些年我踩过的坑
5.1 算法不收敛或迭代慢,先自查这五处
如果你发现fmincon跑了半天还在反复迭代,甚至直接报错不收敛,请按顺序检查:
第一,初始点有没有落在可行域附近。比如权重约束要求总和等于1,你却给了 [0, 0, 0, 0] 这种初值,算法光是把变量拉回可行域就要耗掉一半迭代次数。给合理的平均分布会舒服很多。
第二,变量的量级差得大不大。假设某个变量在10^4量级,另一个在10^-2量级,目标函数对两者的梯度差异巨大,算法在数值上很难保持稳定。解决方法是用缩放变量,让所有变量基本在0到1区间。
第三,约束里有没有NaN或Inf。检查目标函数和约束函数,在边界上会不会出现除零、对数真数为负、根号内部为负数。这些隐蔽数值错误经常是“报告不收敛”的真凶。
第四,有没有太死板的线性依赖。比如权重和为1且收益约束恰好线性相关,导致可行域退化。这类问题可以通过简化约束来规避。
第五,算法与问题规模是否匹配。变量数量长到四位数以上,建议直接换interior-point试试,别抱着sqp不放。
5.2 求解结果明显不对,优先排查的三个方向
如果你觉得结果数值上看似正常,但直觉上完全不对,比如“风险竟然比单买最小风险资产还低”,请优先做三件事:
一是检查约束方向。这在初学时是重灾区,不等式约束方向写反了,求出来的解其实在约束边界外,但算法误以为已经满足条件,返回一个“伪可行解”。
二是检查变量顺序。每个决策变量在目标函数和约束函数中的位置必须完全一致,任何一位错位都会导致结果是乱码,但没有语法报错。
三是尝试不同初值。多设几组分布点,比如从 [1,0,0,0]、[0,1,0,0] 逐个试,观察目标值是否有显著变化。如果变化大,基本可以断定你找到了不同的局部最优,那就得回到第3节说的“粒子群或遗传算法全局初筛”的路线。
5.3 比赛现场的一条急救路径:先跑通线性版本,再逐步加难度
我特别赞成的一个习惯是:拿到题千万别一上来就搞高精尖的非线性模型。先把问题简化成一个线性版本,或者一个极简的非线性版本,确保流程能跑通、结果大体合理,再一步步加回被忽略的约束和非线性项。这样做有几个好处:
第一,方便定位错误。每一步都有对照基准,如果加了某个约束之后结果荒谬,问题大概率出在那个约束上,不必满世界debug。
第二,给后续写作留素材。论文里可以写“为了验证模型的稳健性,我们设计了从简化模型到完整模型的递进测试”,这个叙述逻辑在评委眼里是很加分的。
第三,节省时间。比赛时间极其紧张,先求一个baseline比死磕一个复杂的全局最优更明智。有了baseline,后面优化改参数时也有参照点。
5.4 常见错误速查表
| 错误现象 | 可能原因 | 修复方法 |
|---|---|---|
| 提示“Objective function is undefined” | 目标函数里有NaN或Inf | 在函数里加isnan、isinf断点检查 |
| 迭代到中途不收敛 | 初始点距可行域太远 | 换一个居中的初值,或先用粒子群初筛 |
| 权重和明显不等于1 | 等式约束精度不够 | 在options里增大ConstraintTolerance |
| 结果随初值变化很大 | 问题多峰,fmincon陷入局部最优 | 改用multistart或ga找全局热点 |
| 约束总是无法满足 | 约束方向理解错误 | 在nonlcon里输出g/h到命令行验证 |
| 求解很慢 | 变量量级差异大 | 归一化变量,或调整MaxIterations和StepTolerance |
还有一个容易被忽略的小坑:fmincon返回的exitflag一定要检查。比赛现场很多人只取x和fval,不看exitflag,结果exitflag是-2,说明可行性容差没达到要求,解其实不合格。我的习惯是代码里加一句:
if exitflag <= 0 warning('fmincon未正常收敛,exitflag=%d', exitflag); end能用一个小提醒让自己避免和裁判争论“我数字明明算出来了”。
6. 从“能解”到“拿奖”:论文里怎么写非线性规划
6.1 模型假设和符号体系是门面
写完求解代码之后,写论文时千万别把公式一摆就完事。你要像一个工程师做方案汇报一样,把所有前提条件交代清楚。比如投资组合模型里,需要写“假设各类资产收益率服从联合正态分布”“假设允许任意份额买卖”“暂不考虑交易摩擦”。这些假设看起来啰嗦,但它们决定了模型的可信边界,也是后面你做灵敏度分析的出发点:如果你改变其中一个假设,模型结论怎么变。
符号表更是门面。一个变量多个符号、一个符号多种含义,是评委最容易暴怒的点。我习惯做一个两列三列的markdown表格,把每个变量名、含义、单位、取值方式都列出,正文第一次出现时加粗。
6.2 不要只贴代码,要把“怎么解、为什么这么解”交代清楚
很多同学写论文时喜欢把fmincon求解过程写成一个黑盒子:输入目标函数和约束,输出结果。评委看了只觉得是调用了一个现成函数,很难看出你的建模水平和算法认知。我给的建议是至少写三件事:
第一,说明你选择了什么算法,为什么选它。比如“考虑到问题为中等规模、约束条件以非线性等式与不等式为主,选用Matlab优化工具箱中的SQP算法进行求解”。这一句话就够了,但必须写,因为它展示了你对工具的理解。
第二,展示约束检验结果。算出最优解后,把各个约束的残差列出来,表示解确实满足约束。这个操作很简单,却非常能体现严谨性。
第三,画收敛图。fmincon的迭代信息里包含每步的目标函数值,可以用output.iterations记录画出来。一张“目标函数值随迭代步数下降”的曲线,直观地证明求解过程是健康的,也让评委对你的求解能力产生信任。
6.3 灵敏度分析与稳健性:让论文从“做得对”升级到“做得好”
拿到最优解后,比赛的最优策略不是立刻写完走人,而是抽时间做一组灵敏度分析。
常用的做法很简单:挑一个你认为对结果影响最大的参数(比如目标收益R0、某个资产的预期收益Ri),在合理范围内扫描它,重新求解模型,记录最优解的变化,然后画一条曲线或热力图。
举我们自己写的投资组合的例子,可以对“最低预期收益率”做扫描:
| 最低收益目标 | 最优权重(风险最小) | 组合风险 |
|---|---|---|
| 8% | 0.50, 0.30, 0.20, 0.00 | 0.042 |
| 10% | 0.35, 0.35, 0.20, 0.10 | 0.058 |
| 12% | 0.21, 0.36, 0.10, 0.33 | 0.074 |
| 14% | 0.05, 0.30, 0.20, 0.45 | 0.095 |
| 16% | 0.00, 0.10, 0.25, 0.65 | 0.124 |
这个表放到论文里,比任何空洞的“模型稳定”四个字都有说服力。你能直接看到:随着收益目标升高,资金会向高收益资产倾斜,风险单调上升。这就是经济学里“没有免费午餐”的量化证据。
再进一步,如果时间充足,可以对某些资产收益率做±10%扰动,看看最优权重的变化范围是否可接受。如果某个最优解对参数特别敏感,论文里多写一段“该结果对参数R_i较敏感,实际应用中需要对该参数做精细化估计”——这种表述会让评委觉得你真正理解你的模型,而不是只会跑代码。
6.4 关于Matlab版本的一个小提醒
这几年每年都有人问我要不要装最新版、密钥哪里找之类的问题。我的态度很明确:数模比赛和平时学习,根本不需要追新版本。R2023b、R2024a这一代已经非常成熟,fmincon、ga、particleswarm这些经典函数在近十年版本里几乎没变过,你写出来的代码拿到新老版本上都能跑通,反而省去兼容性的麻烦。
我建议优先使用学校购买的正版授权,或者MathWorks官网的官方试用版,教学科研用途完全够用。比赛现场最怕的不是某个函数不存在,而是换了一台电脑后代码还要改,那才是灾难。所以,与其纠结版本号,不如把自己电脑上的代码写得可移植性更强、注释更清楚。
写在最后的一个私人心得
非线性规划这东西,说到底不是一个靠背函数能学好的知识。我带了这么多届学生,见过太多人把fmincon背得滚瓜烂熟,但一到全新题目就卡在建模这一步。反过来,把实际问题里的“决策变量、目标、约束”这三样分析清楚,再动手写代码,反而能顺得让人惊讶。
我个人比较喜欢的一个工作习惯是:建模两小时,求解十分钟,解释结果两小时。解出来不是终点,能把结果背后的经济含义、约束的松紧程度、参数的敏感性讲给评委听,才算把非线性规划这个工具用透了。
最后再送一个小技巧给正在备赛的朋友:无论题目多宏大,先尝试把一个很小的简化版非线性规划模型跑通。比如把100个变量砍到3个,看看目标函数的趋势、约束的关系是否清晰,然后再一步步把规模加回去。这个“小步快跑”的办法,能帮你避免90%的无效debug,也是我带比赛这些年最想让新手早点懂的一件事。