这个题目我第一次看到时,第一反应是“又一个购电优化模型”。但仔细拆完题目里的三个关键词——主从博弈、多元零售套餐、多级市场购电,你会发现它想解决的问题,和常见论文里那个“给定负荷曲线然后最小化购电成本”的套路完全不是一回事。零售电价不再是外生常数,用户负荷也不再是固定输入,售电商手里真正要决策的,是一整套“先给用户出套餐、再根据用户响应去做批发侧购电”的联动策略。这也是为什么这类题在EI期刊里一直有稳定受众:它把一个真实的业务闭环变成了可计算的数学问题。
这篇文章适合两类人看。一类是电力市场、电气工程方向,手头正好有类似课题需要复现或改造的研究生;另一类是售电公司或综合能源服务商里做策略分析的人,想理解零售套餐和批发购电怎么耦合建模。我会把模型拆成博弈框架、套餐侧建模、购电侧建模、双层转单层求解、Matlab落地几个层次来讲,最后单独聊一段复现过程中最容易翻车的细节。这些都是我自己实际跑模型时踩过的坑,比论文附录里写得要直白得多。
1. 先定价后响应的博弈关系:主从博弈如何刻画售电商与用户的互相牵制
1.1 零售侧的业务时序天然是“售电商先出牌,用户后接牌”
先从业务现象说起。售电商和终端用户之间,本质上存在一个时间先后顺序:售电商先把零售套餐方案摆出来——分时电价、阶梯电价、固定套餐、激励型套餐等等;用户看到这些套餐之后,才会决定自己要选哪个套餐、每个时段用多少电。这种“先报价、后用电”的次序,正是主从博弈(Stackelberg博弈)最典型的适用场景。
为什么不能用普通的纳什同时博弈来描述?因为两边不是同时做决策的。售电商发布套餐在前,用户调整用电行为在后,后动的一方能够观察到先动一方的全部策略,而且先动方在制定策略时也清楚后动方会按照自己的利益最大化去响应。这种信息结构和时序结构,跟Stackelberg博弈的前提完全吻合。
顺带说一个很多人容易混淆的点。有些文献会用“均衡”这个词来描述最终结果,但这里说的均衡不是单纯的纳什均衡,而是子博弈精炼意义上的Stackelberg均衡。简化理解就是:给定售电商的套餐和购电方案,用户已经没有任何动机去换套餐或者改变用电量;给定用户的这种反应规律,售电商也没有动机单独去调整自己的策略。两边都“不想动”,这一组策略组合才是模型要输出的答案。
1.2 上下层目标函数与变量关系:一个闭环里的两层决策
把模型的结构展开是这样的。上层是售电商,决策变量包括:套餐的定价参数(比如分时价格、阶梯阈值、激励补贴),以及各市场购电量(中长期、日前、实时)。目标函数很直接,最大化售电净利润,也就是零售收入减去批发购电成本,再减去偏差考核成本。
下层是用户。每个用户面对一个套餐集合,需要决定两件事:选哪个套餐,以及每个时段用多少电。目标函数是最大化用电效用减去电费支出,也就是消费者的净剩余。
这两层看起来各自独立,实际是咬合在一起的。用户的最优用电量是价格的函数,这个函数会传导到上层,影响售电商的零售收入和批发购电总量;而上层的定价决策又会反过来改变用户的最优响应。可以理解为:上层每一个候选策略,都要先“预演”一遍下层用户的反应,然后才能计算自己的利润。数学上对应的就是下层问题的最优解函数被嵌进上层目标里。
1.3 博弈均衡的现实含义:比静态最优定价更“温和”的价格
讲一个直观例子。如果售电商无视用户反应,直接按静态垄断定价公式算一个最高利润电价,往往价格定得很高。但这种定价忽略了一个事实:电价涨上去之后,用户会少用电、换套餐、甚至不再参与任何零售套餐,需求曲线发生变化,实际收入可能远低于静态模型的预测。
主从博弈的均衡价格通常会比静态垄断定价更温和,原因就在于上层决策者把用户的“用脚投票”行为纳入了自己的目标函数,把价格定在用户还能接受、同时自己利润最大化的位置。这实际上就是需求价格弹性的内生化处理,比外生给一条需求曲线的做法要合理得多。
2. 套餐矩阵设计背后的数学:套餐形态、用户效用与需求弹性
2.1 多元套餐不只是“多设计几个”,而是参数空间的组合
“多元零售套餐”这个词听起来像个营销概念,建模的时候要把它转成结构化的决策对象。常见的套餐形态可以归成几类,每一类都有自己可调的参数维度:
| 套餐类型 | 定价结构 | 典型适用用户 | 主要可调参数 |
|---|---|---|---|
| 分时电价套餐 | 峰、平、谷时段分别定价 | 用电规律性强、可移峰填谷的用户 | 各时段价格、时段划分 |
| 阶梯电价套餐 | 月用电量超过阈值后价格上升 | 大电量用户 | 电量阈值、各阶梯价格 |
| 固定费率套餐 | 一口价加服务费 | 注重账单可预测性的用户 | 固定价格、服务费 |
| 激励型套餐 | 基础电价加削峰补贴 | 可参与需求响应的弹性用户 | 基础价格、激励单价 |
这里要注意,论文里常说的“多元”并不是单纯追求套餐数量多,而是不同类型的参数组合共同覆盖不同的用户群体。建模时每个套餐对应一组连续参数变量,加上一个用户与套餐之间的0-1选择变量,就构成了完整的套餐设计空间。
2.2 用户效用函数:二次效用是大多数文献的默认选择
用户侧建模是整个模型的关键底座。最常见的做法是用二次效用函数来描述用户用电带来的满意程度:
U(q) = a × q - 0.5 × b × q²
这里的a可以理解为用户对电能的边际估值上限,b刻画边际效用递减的速度。这个函数取一阶导数,边际效用MU(q) = a - b × q,是一条向下倾斜的直线。它对应的经济含义是:用户每多用一度电,这度电带来的满足感会逐渐下降,但最初的几度电价值很高。
用户的净收益等于效用减去电费支出。如果某个用户选择了价格为p的套餐,他的净收益最大化问题就是max U(q) - p × q。对q求一阶条件,得到最优用电量:
q*(p) = (a - p) / b
这个表达式非常漂亮,零售电价每提高一点,用户用电量就线性减少一点。而且不同用户的a、b参数不同,同样的价格下,a大b小的用户用电多、对价格不敏感;a小b大的用户用电少、对价格很敏感。这种异质性正是套餐能够差异化定价的依据——售电商想通过不同的价格菜单,把不同类型的用户筛到不同的套餐里去。
2.3 自选择约束与外部选项:用户必须“选且只选一个套餐”
整套模型里还有一个非常容易被忽略的细节——外部选项。用户并不是必须在售电商给出的套餐里选一个,他可以选择不签约,用默认的目录电价或者直接从批发市场购电。这个“退出选项”在建模里对应一个虚拟的套餐0,价格固定、效用固定。
如果模型里没有外部选项,实际上就强制了所有用户都必须参与套餐,这会把价格推到一个不合理的水平,因为售电商有了完全的定价权。加入外部选项之后,用户的参与约束自然变成:只有套餐带来的净效用不低于外部选项带来的净效用,用户才会签约。这在数学上表现为一组线性不等式,同时配合互斥约束(每个用户最多只选一个套餐),构成用户侧完整的选择结构。
套餐互斥约束在代码里的写法是约束每个用户的0-1选择变量和为1或者小于等于1。如果允许用户不参与,就是小于等于1;如果强制参与,就是等于1。我做复现时通常先跑“允许退出”的版本,看参与率是否合理,再决定是否收紧。
2.4 需求弹性推导:为什么低估值用户对价格更敏感
从需求函数q*(p) = (a - p)/b可以顺手推一下价格弹性:
ε = (dq/dp) × (p/q) = -p / (a - p)
看到这个式子就能解释很多定价现象。当p逼近a的时候,弹性趋近于负无穷大——用户估值上限本来就低,你把价格定到接近他的估值上限,他几乎要退出市场,稍微一涨价需求量就剧烈收缩;相反,a远大于p的用户弹性很小,是刚性用户。
这个推导结果对套餐设计有直接指导价值。高价时段应该主要覆盖刚性用户,低价时段才能吸引弹性用户多用甚至转移负荷。我在做模型调参时,经常会先在Excel或者Matlab脚本里把不同用户段的弹性曲线画出来,再回来设定套餐价格变量的初值和上下界,能省去大量调试时间。
3. 批发侧三层购电组合:中长期、日前与实时偏差的成本结构
3.1 售电商为什么不可能只从一个市场买电
购电侧的问题,本质是在多个时间尺度的市场之间做一个组合决策。现实中批发侧的电力交易大致分成三个时间层次:中长期合约市场、日前现货市场、实时平衡市场。三个市场的价格特性和功能完全不同:
| 市场层次 | 时间提前量 | 价格特点 | 功能定位 |
|---|---|---|---|
| 中长期合约 | 提前数周至数月 | 价格相对稳定,波动小 | 锁定基础电量,规避价格风险 |
| 日前市场 | 提前一天 | 反映供需预测,波动大 | 根据预测调整购电组合 |
| 实时市场 | 运行前数分钟 | 波动剧烈,可能有偏差考核 | 平衡实际负荷与日前计划的偏差 |
只从单一市场购电的方案在理论上可行,但要么风险太高(全部暴露在现货价格波动里),要么成本太高(中长期价格通常包含风险溢价)。所以论文里都会做成组合决策:中长期买大部分基础电量,日前市场做修正,实时市场处理预测误差。
3.2 购电模型的数学表达:成本分解与电量平衡约束
购电侧的决策变量是三个市场各自的购电量向量,分别记作Q_l、Q_d、Q_r,每个向量对应24个时段。总购电成本写成:
C_total = Σ [ c_l(t) × Q_l(t) + c_d(t) × Q_d(t) + c_r(t) × Q_r(t) ]
其中c_r(t)是实时市场结算价格,实际模型里经常把实时偏差处理成一个分段函数:多买和少买的惩罚系数不一样,因为不同交易机制对正负偏差的考核不对称。这个细节看起来小,但对结果影响很大。我在第一次复现时偷懒只用了单一偏差价格,结果最优解总是倾向于在实时市场大量买入,明显不合理。
核心约束是电量平衡,也就是每个时段的套餐供电量等于三个市场的购电量之和:
Q_l(t) + Q_d(t) + Q_r(t) = D_total(t)
这里D_total(t)不是外生常数,而是下层用户优化后的总用电量。这就是上下层耦合的另一个通道:用户套餐选择直接影响D_total,D_total反过来决定购电组合,购电成本又进入上层目标函数。
3.3 不确定性处理:期望值模型、场景法与风险度量
购电决策面临的最大现实问题是不知道明天的负荷曲线和现货价格。论文里最常见的处理方式有两类。
一类是期望值模型,把所有随机变量取期望值,把问题变成确定性问题。优点是求解快,缺点是风险中性,完全忽略极端场景。这类模型适合做基准对比。
另一类是两阶段随机规划,在日前决策时生成多个现货价格场景和负荷场景,目标函数变成期望收益,或者用条件风险价值(CVaR)控制尾部风险。这类模型在理论上更完整,但求解规模会成倍增长。复现这类题目时,我建议先用期望值模型把整个逻辑跑通,再逐步加场景数,否则一上来就做几十个场景的随机规划,问题规模很容易让求解器直接卡死。
4. 双层优化求解的硬骨头:KKT条件替换与混合整数线性化
4.1 为什么双层模型不能直接交给求解器
很多初学者拿到这类模型,第一反应是“这写出来不就是个优化问题吗,扔给求解器跑就是了”。但双层优化和单层优化有个本质区别:目标函数里嵌套着另一个优化问题。这种嵌套结构让整体问题一般不满足标准数学规划的形式,而且很可能非凸,求解器拿到之后根本没有明确的方法可以处理。
主流的处理思路有三条。第一条是对下层问题用KKT最优性条件替换,把下层优化变成一组等式和不等式约束,从而把双层问题转成单层问题;第二条是用迭代启发式,上层给个策略,下层求最优响应,再反馈给上层调整;第三条是用元模型或查表近似下层反应函数。对于零售套餐这种下层可以解析或者凸化的问题,KKT替换是精度和效率最平衡的方案。
4.2 下层问题的KKT条件:从“优化问题”到“约束条件”
以下层用户的用电量决策为例。用户的问题是最大化U(q) - p × q,没有复杂的约束条件时,最优性条件就是一阶导数等于零。但一旦引入套餐互斥约束、用电上下限约束之后,下层问题就需要完整写出KKT条件:拉格朗日函数对各变量的一阶偏导为零,配合互补松弛条件。
这里有个前提必须说明:下层问题必须是凸优化问题,KKT条件才是充分必要条件。好在二次效用函数是凹函数,最大化凹函数等价于凸优化,下层问题天然满足这个要求。这也是为什么文献里几乎清一色用二次效用来建用户模型——换一个非凸效用函数,KKT替换就未必成立了,整个求解路线都要推翻重来。
4.3 互补松弛条件的线性化:大M法的具体操作
KKT条件里最棘手的是互补松弛条件,形式是0 ≤ λ ⊥ g(x) ≥ 0,含义是拉格朗日乘子λ和约束剩余量g(x)不可能同时为正。这个条件本身是双线性的,不能直接放进线性规划或混合整数线性规划。
标准做法是大M法:引入一个0-1辅助变量z,把互补条件拆成两个约束:
λ ≤ M × z
g(x) ≤ M × (1 - z)
如果z等于0,强制λ等于0,g(x)可以自由;如果z等于1,强制g(x)等于0,λ可以自由。两边的约束都被M这个足够大的常数“托底”,从而把非线性的互补关系变成混合整数线性约束。
大M的取值是个经典工程问题。取太小,可能把真正的可行解排除掉;取太大,数值稳定性变差,求解器在整数分支时容易犯病。我的经验是先看目标函数和约束里所有变量的数量级,M取最大数量级的100倍左右,然后跑一次模型检查互补残差,残差超过1e-4再调大。
4.4 双线性项的处理:套餐选择变量与连续变量的乘积
KKT化之后还有一个隐蔽的非线性来源——用户选择变量的乘积项。比如用户的套餐选择变量是0-1变量,套餐里某个时段的用电量是连续变量,二者相乘在目标函数里出现时,就变成了双线性项。
处理这种项有两条路。一条是引入辅助变量,让辅助变量等于连续变量与0-1变量的乘积,再用两组大M约束把它夹住;另一条是直接利用下层问题的性质,把用电量表示成价格的解析函数,绕开乘积项。前者通用但增加变量数量,后者只在能解析推导出反应函数时可用。我在实操中倾向于先用解析法验证小规模场景,再上通用大M法做完整模型,两边的结果如果对不上,问题几乎一定出在大M值的选取上。
4.5 三种求解路径的取舍:除了KKT还有什么选择
和很多做这个方向的同学交流下来,最常用的替代方案是交替迭代法:先固定套餐价格求用户最优用电量,然后固定用户响应去优化购电组合和套餐价格,反复迭代直到收敛。这个方法的优点是好实现,缺点是没有收敛性保证,而且容易收敛到不稳定的伪均衡。
| 求解路径 | 实现难度 | 收敛保证 | 适用场景 |
|---|---|---|---|
| KKT单层化 | 中高 | 有(需凸性支持) | 论文级精确求解,小规模模型 |
| 交替迭代 | 低 | 无严格保证 | 快速验证逻辑,大规模近似 |
| 元模型近似 | 高 | 取决于近似精度 | 非线性复杂响应的场景 |
我的建议是:毕业设计、论文复现这类需要严谨结论的场景,尽量用KKT单层化;如果是工程上的快速策略评估,交替迭代完全够用。
5. Matlab实现骨架:从数据准备到YALMIP求解的代码级拆解
5.1 工程目录与场景数据准备
先说说整体工程结构。复现这类模型,我习惯把代码分成四个模块:参数设置、场景生成、模型求解、结果绘图。目录结构大致是这样:
retail_stackelberg/ ├── main.m % 主入口,控制整体流程 ├── config_params.m % 所有基础参数设置 ├── gen_scenarios.m % 生成用户参数与市场价格场景 ├── build_model.m % 用YALMIP构建优化模型 ├── solve_model.m % 调用求解器并处理结果 └── plot_results.m % 绘制电价曲线、购电组合、用户响应数据准备阶段最关键是生成用户参数矩阵。每个用户的估值参数a和b不能随便生成,建议先定义几个用户类型(刚性、普通、弹性),每类一个均值,再在均值附近加噪声。这种生成方式贴近现实,而且后续做敏感性分析时能说清楚是哪种用户影响了结果。
% 用户类型定义:刚性、普通、弹性 a_mean = [1.8; 1.2; 0.8]; % 边际估值上限,单位:元/度 b_mean = [0.25; 0.4; 0.6]; % 边际效用递减速度 user_type = repmat(1:3, 1, 20)'; % 60个用户,每类20个 a_param = a_mean(user_type) + 0.05*randn(60,1); b_param = b_mean(user_type) + 0.03*randn(60,1); % 保证参数非负 a_param = max(a_param, 0.3); b_param = max(b_param, 0.1);5.2 核心建模代码片段:变量声明与主约束的YALMIP写法
模型构建是整个复现的核心。下面我给出一个经过简化的骨架,保留最关键的部分,你可以把它当模板改成自己的完整模型。
T = 24; % 时段数 N = 60; % 用户数 M = 4; % 套餐数,含外部选项 %% 决策变量声明 p_retail = sdpvar(T, M, 'full'); % 各套餐各时段零售电价 Q_long = sdpvar(T, 1); % 中长期购电量 Q_day = sdpvar(T, 1); % 日前市场购电量 Q_real = sdpvar(T, 1); % 实时市场购电量 z_select = binvar(N, M, 'full'); % 用户选择套餐标记 % 用户各时段用电量,注意该变量同套餐选择相关时会产生双线性项 L_use = sdpvar(N, T, M, 'full'); %% 约束:用户选择约束 Constraints = []; % 允许用户不参与任何零售套餐,所以每行之和小于等于1 Constraints = [Constraints, sum(z_select, 2) <= 1]; %% 约束:用电量只在已选套餐下非零(大M形式) M_big = 50; for n = 1:N for m = 1:M Constraints = [Constraints, L_use(n,:,m) <= M_big*z_select(n,m)]; end end %% 目标:售电商净利润 revenue = 0; for n = 1:N for m = 1:M revenue = revenue + p_retail(:,m)' * squeeze(L_use(n,:,m)); end end cost_buy = c_long'*Q_long + c_day'*Q_day + c_real'*Q_real; profit_expr = revenue - cost_buy; %% 购电平衡约束(简化为各时段总和相等) for t = 1:T user_demand_t = 0; for n = 1:N for m = 1:M user_demand_t = user_demand_t + L_use(n,t,m); end end Constraints = [Constraints, user_demand_t == Q_long(t)+Q_day(t)+Q_real(t)]; end这里有两个地方必须补充说明。第一,上面代码里revenue的计算依赖L_use乘以p_retail,它们之间存在耦合关系,严格建模时要引入辅助变量把p_retail和L_use的乘积线性化,否则目标函数里会混入双线性项。第二,用户下层优化的KKT条件没有体现在这段代码里,完整模型必须把KKT约束追加到Constraints中,才能保证用户行为是“最优响应”而不是随意取值。
5.3 KKT条件线性化的实现要点
把下层用户的KKT条件写进模型,是篇幅最大、最容易写错的部分。下面给出互补松弛条件线性化的标准写法:
% 假设下层用户优化存在约束:用电量不超过最大允许量 Lmax(n) % 拉格朗日乘子 mu 对应约束 L_use <= Lmax mu = sdpvar(N, M, 'full'); z_kkt = binvar(N, M, 'full'); % 互补条件辅助变量 % 互补条件:mu >= 0, Lmax-L_use >= 0, 两者至多一个为正 M_kkt = 20; Constraints = [Constraints, mu >= 0]; Constraints = [Constraints, Lmax - L_use <= M_kkt*(1-z_kkt)]; Constraints = [Constraints, mu <= M_kkt*z_kkt]; % 最优性条件:边际效用 - 零售价格 + 乘子相关项 = 0 for n = 1:N for m = 1:M Constraints = [Constraints, a_param(n) - b_param(n)*L_use(n,:,m) ... - p_retail(:,m)' + mu(n,m) == 0]; end end注意上面这个写法是高度简化的示意版本。真实模型中,用户问题还包含套餐选择变量,KKT条件需要针对每个可能的选择分支单独写,这里体现出来的复杂度比论文里几句话要多得多。这也是为什么复现这类模型时,最花时间的环节是在KKT约束的调试上。
5.4 求解器选择与运行环境建议
求解这块,YALMIP本身只是一个建模层,真正干活的是底层的混合整数求解器。我实测下来,Gurobi和CPLEX对这类带大量0-1变量的混合整数二次规划支持最好。如果你没有商业求解器授权,Matlab内置的intlinprog可以跑比较小的例子,但规模稍大就非常吃力。
变量规模是一个硬约束。一个60用户、24时段、4套餐的模型,加上KKT条件引入的辅助变量和互补松弛辅助变量,变量数量很容易超过上万个,其中包含大量0-1变量。YALMIP建模时不觉得,求解时才会发现内存和时间的压力。建议先跑5用户、6时段的微型场景调通逻辑,再逐步放大。
6. 复现中常见的翻车点与结果合理性检验清单
6.1 大M值引发的“隐性错解”
这是复现时最隐蔽的坑。前面提到大M取太大或太小都有问题,但实际跑的时候你不会立刻发现,因为求解器依然会返回一个“最优解”。怎么判断解是不是错的?看互补松弛条件的残差。
我踩过一次坑,模型跑出来套餐价格全跑到边界上,利润高得离谱,直觉上就有问题。查了半天发现是某个互补约束的大M取值小了,导致一个本来应该激活的约束被错误松弛掉,求解器在一个扩大的可行域里找到了假最优解。从那以后我每次跑完都会单独检查一组互补残差,打印出所有“乘子和约束剩余同时为正”的违规点。
6.2 整数变量与非线性约束的规模爆炸
用户选择问题引入了一大批0-1变量,这些变量和连续变量的交互又带来额外的辅助变量,问题是逐步膨胀的。实际跑下来,套餐数量从3个增加到5个,求解时间可能从几十秒涨到几十分钟,而且越到后面每加一个变量,求解时间的增幅越是惊人。
控制规模有几种思路。一是压缩时段维度,把24小时聚合成峰、平、谷三类时段;二是对同质用户做聚合,每个类型只保留一个代表性用户;三是限制KKT条件只针对活跃套餐分支枚举。这些做法都会损失一点精确度,但换来的是求解过程真正可跟踪,适合调参阶段反复试错。
6.3 购电平衡约束与套餐耦合时的可行域问题
购电侧和套餐侧的耦合经常导致模型出现隐藏的不可行。常见情况是:购电平衡约束要求每个时段总供电量和用户用电量相等,但用户的用电量又受套餐选择约束限制,如果套餐价格定得太低,用户用电量暴涨,而购电侧合约上限又卡得死,整个模型直接无可行解。
遇到这种情况,我的排查顺序是:先固定套餐价格为某个给定值,单独求解购电子问题,确认购电侧可行;再固定购电方案,单独求解用户侧子问题,确认用户选择可行;最后才把两边合起来。哪一步挂了问题就在哪一步,这种分而治之的排查方式能省很多时间。
6.4 一套基础的结果合理性检验清单
跑通模型只是第一步,判断结果是否合理更重要。我每次做完一个完整实验,会至少检查下面几项:
| 检查项 | 判断标准 |
|---|---|
| 用户参与率 | 套餐或外部选项的参与比例应处于合理区间,通常不应该是100%全签约 |
| 零售收入vs购电成本 | 净利润应为正,且对购电成本的覆盖率在合理商业范围内 |
| 价格响应方向 | 提高某时段电价,该时段用户用电量应下降 |
| 套餐内部一致性 | 高价套餐的用户用电量应该低于低价套餐,除非用户类型差异过大 |
| 渐进性 | 从单一时段模型扩展到多时段模型,结果不应出现数量级的突变 |
如果这几项里有两项以上异常,先别急着调求解参数,大概率是建模逻辑有问题,回到约束条件重新检查更靠谱。
6.5 数据生成细节对结果的高度敏感性
最后说一个容易被低估的问题:整个模型对用户参数和市场价格的生成方式极度敏感。同样一套主从博弈框架,把用户估值参数的方差从0.05改成0.2,最优套餐结构可能从“三档分时”变成“全部统一价”,结论完全反过来。
所以复现别人的论文时,除了复现代码,严格来说你还得复现他的数据生成过程。很多论文不公布完整参数,这时候就要自己对参数做敏感性分析,把关键结论的适用边界说清楚。我在写这类题目的报告时,会把参数生成脚本和模型代码一起存档,保证任何一次实验都能完全复现,而不是靠运气跑出一个漂亮结果。
做完整套复现之后,我对这类题目最大的感受是:它的核心难点不在数学推导本身,而在把业务假设转成数学约束的过程里。同一个人,同一个Matlab版本,同一套求解器,如果你对“用户能不能退出套餐”这种细节的假设不同,跑出来的策略可能是天壤之别。所以如果你也正在做类似课题,我建议在动手写代码之前,先把业务假设一条条列出来:用户是否必须签约、偏差电量是否允许超买、中长期购电比例是否能跨时段交易。把这些细节想清楚了,后面所有代码和调试都会顺畅得多。