
前阵子A同学抱着一篇硕士论文来找我说是照着正文里的公式和表格敲了三天Matlab结果画出来的调度曲线跟论文里那张经典图差了十万八千里。我看了一下他的代码问题不全在编程而是很多调度模型里的默认假设、单位换算、场景处理细节在论文里被一笔带过了。这其实是“论文复现”最典型的状态大框架写得明明白白落到代码层面全是暗坑。这篇博文就围绕“可再生能源发电与电动汽车协同调度”的论文复现过程来写把我实际跑通这类课题的完整思路、建模取舍、Matlab实现细节和排查经验一次性讲清楚。你要是正在做类似方向——不管是课程设计、毕业课题还是准备往碳中和、电力市场方向靠——这篇文章能帮你省掉大量翻论文和试错的时间。1. 复现第一步先把论文的“故事线”拆出来很多人拿到一篇论文就开始码代码这种习惯在复现场景下特别致命。论文是压缩过的研究叙事不是技术规格书。你看到的公式和参数只是作者在大量调试之后选定的“最终形态”背后的取舍、试探性尝试、甚至笔误都不会写在纸面上。所以我的第一个建议是在打开Matlab之前先用半天时间做论文拆解。1.1 论文模型的隐藏假设先别急着跑代码做复现首先要回答四个问题这篇论文做的是确定性调度还是不确定性调度如果正文里出现了“场景”“概率”“期望值”这类字眼那大概率是随机规划框架。这时你要找到场景集合是怎么生成的用的是蒙特卡洛抽样、拉丁超立方还是真实历史数据聚类。不同方法生成的场景特征差别很大直接影响最终调度结果。决策变量有哪些协同调度里必然是机组出力和电动汽车充放电功率都要优化。但有的论文把EV充放电当成可连续调节的变量有的则退化成“只能以固定功率充电”后者近似成0/1整数变量更合理。这个区别决定了你的模型是线性规划LP、混合整数线性规划MILP还是非线性规划NLP进而决定你用什么求解器。目标函数是单目标还是多目标绝大多数硕士论文用的是单目标加权最常见的是“运行成本弃风弃光惩罚EV调度成本”。但也有不少论文把碳排放单独拎出来做成多目标。单目标复现简单但你要特别注意权重系数——很多论文不公布权重这时候只能靠参数敏感性分析反推。时间尺度是什么一天24小时还是96个15分钟时段EV用户出行行为和风电出力的波动特征在不同时间粒度下差异巨大。以15分钟为单位机组爬坡约束变得更重要以小时为单位EV调度的灵活性会被低估。这四个问题都有明确答案之后再列一张表把论文里的每个模型要素和对应参数整理出来。我当时做这类复现时的表格列大概是这样的模型要素对应参数论文中的写法复现时的处理方式风电出力预测值/实际值Weibull分布抽样改由场景生成模块统一输出光伏出力时段辐照度晴空模型加误差与风电场景联合抽样常规机组燃料成本系数二次函数分段线性化处理EV可调度容量电池容量/SOC统一取均值按区域聚合模型近似旋转备用备用比例负荷的5%~10%加入备用约束需确认比例这里最关键的一条经验论文里所有带数字的句子都值得你画一条线标记出来因为很多时候数字跑不出来就是漏了某个不起眼的约束或比例系数。1.2 场景与参数的对应关系代码里数字从哪里来复现的另一个大坑是搞不清数据来源。论文里写的“某实际区域数据”“某电网数据”往往含糊其辞。我处理这个问题的原则是能查到原文数据集的就去查原文引用的数据查不到的用同类公开基准系统数据代替并且在复现说明里写明“参数取自XX替代数据”不影响模型结构的验证。比如常规机组参数论文如果没有特别说明大概率是IEEE 30节点或IEEE 118节点系统的公开数据。EV参数则通常参考的是通用电动汽车数据电池容量约40-60kWh充电功率3.5-7kW。风电和光伏的预测误差一般假设成正态分布标准差取预测值的10%-20%。你需要建立一个“参数来源清单”每个参数都记录清楚从哪里来、如何设定、为何这么设。这样万一结果对不上排查范围就大大缩小了——不用怀疑每个参数都错了只需要检查那些“设定值比较敏感”的参数。2. 协同调度的核心数学表达目标函数与约束的取舍跑通协同调度模型本质上就是把“在满足系统安全稳定运行的前提下让可再生能源出力尽量被消纳、EV用户调度尽量合理”翻译成一组数学表达式。论文复现的难点不在于能不能写出这组表达式而在于你写的表达式和论文作者在代码里实际用的表达式完全一致。2.1 目标函数怎么选才对得上论文协同调度最常见的目标函数是日运行总成本最小化包含四部分常规机组燃料成本是绝对的大头通常写成二次函数 [ C_{fuel}\sum_{t1}^{T}\sum_{i1}^{N_g}\left(a_iP_{i,t}^{2}b_iP_{i,t}c_i\right) ]这里的 (a_i,b_i,c_i) 是第 (i) 台机组的燃料成本系数(P_{i,t}) 是机组 (i) 在时段 (t) 的有功出力。弃风弃光惩罚成本数学上写成 [ C_{curtail}\sum_{t1}^{T}\lambda_{curtail}\left(P_{t}^{RE,avail}-P_{t}^{RE,use}\right) ] 其中 (P_{t}^{RE,avail}) 是可再生能源在时段 (t) 的可用出力(P_{t}^{RE,use}) 是实际调度出力。惩罚系数的选取很关键——定得太低模型宁可弃风也不调用昂贵机组定得太高又会让系统不计成本地消纳新能源。常规做法是取火电出力的2到5倍常见范围在500-2000元/MWh之间。EV调度费用则更有讲究。有的论文把EV当成单纯的柔性负荷充电服务费直接用固定电价有的论文考虑了V2GVehicle-to-Grid即电动汽车向电网反向放电这时候就要加上放电损耗成本甚至计及电池循环老化成本。我在复现时发现加不加电池寿命折损项会显著改变EV的调度行为——没有折损项时模型会肆无忌惮地让EV一天之内反复充放电这个结果在理论上是“最优”的但在工程上完全没有可行性。如果你发现论文里没有提电池老化约束那作者很可能只设置了“一天最多充一次电”的简化规则。复现的时候你也要按这个简化规则来先对上论文的图再考虑改进。2.2 约束条件里的几个“坑”爬坡、SOC、V2G约束条件比目标函数更容易出错因为任何一个约束漏掉优化结果都会悄悄偏掉。我按踩坑频率从高到低列一遍功率平衡约束字面上最基础 [ \sum_{i1}^{N_g}P_{i,t}P_{t}^{RE,use}\sum_{v}P_{v,t}^{dis}P_{t}^{load}\sum_{v}P_{v,t}^{ch} ]但这里的 (P_{t}^{load}) 很容易搞错。很多论文用的基础负荷中到底包不包含EV充电负荷如果不包含那么EV充电是额外新增的电量需求如果包含那平衡约束里写错任何一个都会导致结果看起来“可用电量多了很多”从而给出一份过于乐观的调度方案。机组爬坡约束 [ -\Delta P_{i}^{down}\le P_{i,t}-P_{i,t-1}\le \Delta P_{i}^{up} ]这句看起来平平无奇但很多人忽略了下标 (t-1) 对应的基准值。如果 (t1) 时 (t-1) 取的是前一天最后一个时段的出力那么这个初始值论文里若不强调你自己必须做出一个合理估计。我当时复现某篇论文时就是因为这个初值设成了“当日首个时段的出力下限”导致前两小时机组爬坡约束长期被违反。EV的SOC与充放电逻辑是重灾区[ SOC_{v,t}SOC_{v,t-1}\eta_{ch}P_{v,t}^{ch}\Delta t-\frac{P_{v,t}^{dis}\Delta t}{\eta_{dis}} ][ 0.2\le SOC_{v,t}\le 0.9 ][ P_{v,t}^{ch}\le X_{v,t}^{ch}P_{v,\max}^{ch},\quad P_{v,t}^{dis}\le X_{v,t}^{dis}P_{v,\max}^{dis} ][ X_{v,t}^{ch}X_{v,t}^{dis}\le 1 ]麻烦在于EV车辆数量很大逐辆建模会让模型规模爆炸。论文里常用的简化策略是“聚合模型”——把所有EV看成一个大的虚拟电池用总容量和总最大功率代替单车的物理参数。这种聚合方式我已经复现了很多次好处是求解速度快坏处是它天然忽略了个体用户的出行需求约束。有的论文会在聚合模型基础上额外加一条“末时段SOC恢复初始值”的约束用来保证EV第二天还有足够的电量继续出行这个细节非常值得加进去。V2G的额外限制更是如此——V2G不是免费午餐电池每放一次电都是循环寿命损耗。即便论文目标函数里没写电池损耗成本也至少有“日充放电次数上限”或者“每辆车一天最多只允许一个完整充放周期”这样的约束。你要是没加这类限制模型给出的方案很可能是EV在深夜电价最低时充满白天高峰时全部放光看起来风机利用率提升了实际上完全脱离车辆使用的现实逻辑。3. 不确定性建模场景生成与削减的不同打法可再生能源出力不可能是精确的风电有波动光伏有云层遮挡所以协同调度论文十有八九走的是“随机规划”路子用大量可能的出力场景来描述不确定性然后在期望成本最小的意义下做出调度决策。3.1 场景生成的方法选择蒙特卡洛还是拉丁超立方场景生成最基础的方法是蒙特卡洛抽样。假设风电预测误差 (\varepsilon_{t}\sim N(0,\sigma_t^2)) 每次抽样就得到一个风电出力误差序列加上预测值就得到了一个完整的出力场景。光伏类似只是误差分布往往不带对称性用Beta分布更合适。但纯蒙特卡洛抽样有个毛病为了覆盖极端情况需要抽很多样本计算量太大。用拉丁超立方抽样LHS替代纯随机抽样是个更容易被忽略但对复现效果影响很大的操作。LHS的基本思路是把每个变量的分布区间等分成N个不重叠的小区间然后从每个区间内均匀抽取一个样本点。这样做的好处是整体上点的分布比纯随机抽样更加均匀能在样本数量少得多的情况下保持同样的覆盖率。我在复现时用LHS取200个场景效果往往比蒙特卡洛抽800个场景还要稳定。3.2 场景削减的实用步骤场景太多求解器扛不住必须削减。最常用的削减算法是同步回代消除法Backward Reduction核心思想是每次迭代都找到最“多余”的一个场景把它删除并用距离它最近的场景替代它重新归一化剩余场景的概率。具体步骤计算所有场景两两之间的几何距离常用欧式距离按所有时段累计。找出最小距离对应的场景对删除其中一个。被删除场景的概率累加到保留的那个场景上。重复直到剩余场景数达到预先设定目标。Matlab里这段逻辑不难实现。关键是场景数量的选取——我做了一组测试场景数从10个加到30个结果方差明显下降再加到50个最优目标值变化趋缓超过50个以后求解时间直线上升结果改善有限。所以复现这类论文时我一般取30到50个削减后的场景既能对得上论文里的结果又不至于把求解时间拖到无法接受。function [scenario_reduced, prob_reduced] scenario_reduction(scenarios, prob, target_count) % scenarios: 每个场景是Nt维向量按行排列 % prob: 每个场景的初始概率 n_scen size(scenarios, 1); while n_scen target_count % 计算两两距离 dist zeros(n_scen, n_scen); for i 1:n_scen for j i1:n_scen dist(i,j) norm(scenarios(i,:) - scenarios(j,:)); dist(j,i) dist(i,j); end end % 找距离最近的一对 dist(dist 0) inf; [~, pair_idx] min(dist(:)); [row, col] ind2sub([n_scen, n_scen], pair_idx); % 删除其中一个概率合并 scenarios(row, :) []; prob(row) []; prob(col) prob(col) prob(row_old); % 这里注意要先保存 n_scen n_scen - 1; end scenario_reduced scenarios; prob_reduced prob / sum(prob); end上面这段代码我故意保留了“row_old”未定义的状态因为实际写的时候容易在这里踩坑删除一个场景后索引全部变了但你手头还拿着删除前的索引去找概率值就会张冠李戴。正确的做法是先保存被删除场景的概率再删除再累加。这类小bug在复现过程中出现频率非常高调试起来特别费神。3.3 削减后的场景质量怎么判断判断削减质量不能只看代码跑不跑得通还要看概率分布的统计特征是否保持一致。我的习惯是削减前后各算一遍每个时段风电出力的期望值和标准差看看相对误差是否在可接受范围内期望值误差控制在2%以内标准差误差控制在5%以内。这个验证步骤看起来费一点力气但它能在模型求解之前就帮你筛掉大量不合理的场景集比等到最后结果对不上再回头排查场景问题要高效得多。4. MatlabYalmip求解链路从建模到出结果的完整流程你问我为什么复现这类论文首选Matlab而不是Python核心原因是Yalmip这个建模工具箱配合Cplex/Gurobi可以把“数学模型”和“求解代码”之间的对应关系保持得非常清楚。论文里的公式长什么样代码里基本就长什么样改起来不容易秃头。4.1 求解器选择与建模框架搭建先看论文的模型类型纯线性模型LP/QP用Cplex就够了速度快免费学术版也够用。含0/1整数变量MILP/MIQP必须用Gurobi或Cplex的商业版本Matlab自带的intlinprog也能跑小规模问题但超过几百个整数变量之后性能差距极大。我测试过一个小算例同样的协同调度模型含120个整数变量intlinprog跑了将近20分钟Gurobi只用90秒。所以如果你的模型规模比较大老老实实去申请Gurobi学术许可这是整个复现过程中性价比最高的一个决定。% 用Yalmip定义决策变量 P_g sdpvar(N_g, N_t, full); % 常规机组出力 P_w sdpvar(N_w, N_t, full); % 风电实际调度出力 P_pv sdpvar(N_pv, N_t, full); % 光伏实际调度出力 P_ev_ch sdpvar(N_ev_agg, N_t, full); % EV聚合充电功率 P_ev_dis sdpvar(N_ev_agg, N_t, full); % EV聚合放电功率 SOC sdpvar(N_ev_agg, N_t, full); % EV聚合荷电状态 X_ch binvar(N_ev_agg, N_t, full); % 充电状态0/1变量 X_dis binvar(N_ev_agg, N_t, full); % 放电状态0/1变量这个变量定义顺序是我多次试验后觉得最舒服的先功率再状态量最后0/1决策。顺序不要乱不然写约束时回头找变量很痛苦。4.2 论文复现中的关键代码结构用Yalmip建模约束写法跟数学公式几乎是一一对应的Constraints []; % 功率平衡约束 Constraints [Constraints, sum(P_g,1) P_w P_pv sum(P_ev_dis,1) ... P_load(1:N_t) sum(P_ev_ch,1)]; % 机组出力上下限 Constraints [Constraints, P_g_min P_g P_g_max]; % 爬坡约束 Constraints [Constraints, -P_ramp_down diff(P_g,1,2) P_ramp_up]; % EV充放电互斥约束 Constraints [Constraints, X_ch X_dis 1]; Constraints [Constraints, 0 P_ev_ch X_ch * P_ev_ch_max]; Constraints [Constraints, 0 P_ev_dis X_dis * P_ev_dis_max]; % SOC递推约束 for t 2:N_t Constraints [Constraints, SOC(:,t) SOC(:,t-1) ... P_ev_ch(:,t)*eta_ch*delta_t - P_ev_dis(:,t)*delta_t/eta_dis]; end % SOC上下限约束 Constraints [Constraints, SOC_min SOC SOC_max];这里有两个容易出问题的细节一是diff函数在Matlab里的行为。diff(P_g,1,2)算出来的是 (t1) 时刻减 (t) 时刻的差值共 (N_t-1) 列所以约束向量需要和这个维度对齐别不小心写成1:N_t。二是约束循环里的 t 从几开始。t1时刻的SOC是已知初始状态递推只能从t2开始。我见过不少人从t1开始写递推直接把第1个时段的SOC约束写成了“SOC(1)等于SOC(0)加充放电增量”然后报维度错误或者出现NaN排查半天找不到原因。目标函数部分如果机组燃料成本是二次函数而你的求解器只支持线性那就需要做分段线性化这在Yalmip里可以用社区贡献的分段函数工具完成也可以用binvar加implies手写分段逻辑。但从复现角度很多硕士论文为了简化求解过程直接用的是“成本按平均煤耗率近似成线性”的简化写法这也能对上论文结果所以复现前务必看一眼论文里的目标函数到底是二次还是线性。4.3 求解选项和结果提取的固定套路调用Gurobi求解时的选项设置也别乱来我常用的配置是ops sdpsettings(solver,gurobi,verbose,0); ops.gurobi.MIPGap 0.001; % MIP间隙 ops.gurobi.TimeLimit 300; % 最大求解时间 ops.gurobi.Threads 4; % 并行线程 optimize(Constraints, Objective, ops);MIPGap设到0.001就行再小意义不大。TimeLimit设300秒在复现阶段完全够用——如果300秒内解不完说明模型规模失控优先检查有没有多余的整数变量而不是干等。求解完成之后用value()提取变量注意Yalmip中如果模型有多个解value()返回的是当前最优解但如果你后面还要做灵敏度分析千万别在循环外缓存value()的结果——每次重新求解后都要重新提取这是个容易踩的低级错误。5. 复现结果对不上论文完整排查链路这是我自己复现时几乎每次都少不了的环节。代码能跑通只是第一步结果能对上论文图表才算复现成功。如果你画出来的曲线和论文里的趋势一致但数值偏差明显或者干脆形状都对不上这时候最需要的是结构化排查。5.1 数字对不上的根因定位从目标函数到约束逐项拆解我会按如下顺序排查第一步检查目标函数值的数量级。论文里写的日运行成本是几万元你算出来只有几千元那绝对不只是参数差异更可能是哪个单位搞错了。风电出力单位是MW还是kWhEV容量单位是MWh还是kWh这一步能排除一半以上的低级错误。第二步检查弃风弃光是否出现异常高的时段。如果某个时段弃风率特别高优先看负荷低谷时段凌晨2点到6点。这个时段EV大概率在充电机组最小出力压不下去弃风是正常的。如果弃风发生在下午那大概率是功率平衡约束或机组出力下限写错了。第三步逐条检查约束边界是否被活跃触发。Yalmip中约束本质上是一个等式或不等式表达式求解完成后你可以用check(Constraints)直接看残差。这个命令会返回每条约束的最大残差和违反量。我实际排查时发现论文复现结果对不上的原因里十有六七是某条约束写反了方向或者上下限写反了。第四步复用论文数据的对标测试。把论文中某个典型时段的输入数据手算一遍写出一个小规模单时段模型来验证逻辑。比如只保留2台机组、1个EV聚合体、1个风电场景手动推一遍最优出力分配再和代码跑出来的结果对比。这种方式能把模型逻辑和数据处理误差完全剥离开是我最推荐的做法。下面表里是我总结的常见差异与对应原因复现现象最可能的根因总成本比论文高很多EV单位调度成本系数设大了或惩罚系数偏低总成本比论文低省略了部分约束模型过于乐观弃风率明显偏高机组最小出力参数偏大或爬坡初值错误EV充放电动作频繁缺少充放电次数上限或电池老化成本某一时段的调度结果异常跳变爬坡约束的维度错位或初值异常求解时间远超论文声称的时间场景削减不足或者整数变量过多5.2 从复现到进阶可扩展的算例设计思路复现跑通之后建议不要停在“结果一致”这一层。硕士论文的图通常是某个基准系统下的单一算例你可以在此基础上做三个方向的扩展这个过程会让你对这类协同调度的本质理解提升一个台阶一是多场景日类型对比。比如工作日和周末的EV出行规律不同充电负荷曲线完全不同。把原来固定不变的EV参与约束改成“工作日可调度EV数量多、周末少”就能比较真实地反映出行行为对调度策略的影响。二是储能与EV协同扩展。加入集中式储能系统后储能和V2G之间谁能更高效地承担削峰填谷的角色这个对比结论非常适合作为复现论文后的新发现写进报告。三是多目标权衡与帕累托前沿。把碳排放和运行成本分别作为两个目标用加权法扫一遍权重系数画出一条帕累托前沿。这样你就在复现之外给论文增加了额外的研究成果答辩时也更有底气。6. 复现过程中的工具链整合与工程化习惯最后这部分讲点偏工程的东西核心观点是复现一篇论文不光是跑通一个模型还包括把你自己的工作时间从杂乱状态中解放出来。6.1 模块化代码结构远比“一个大脚本”可靠我刚开始复现这类论文时习惯把所有代码压进一个脚本跑完就算了。后来发现一个大脚本超过300行之后每次调参和排查错误都变得极其痛苦。后来我改成了下面这种模块化结构optimization_project/ ├── data/ │ ├── load_data.m % 负荷曲线数据 │ ├── generator_data.m % 机组参数 │ ├── ev_data.m % 电动汽车参数 │ └── wind_solar_data.m % 新能源出力数据 ├── scenario/ │ ├── generate_scenarios.m % 场景生成 │ └── reduce_scenarios.m % 场景削减 ├── model/ │ ├── build_model.m % 构建优化模型 │ └── solve_model.m % 求解并输出结果 ├── result/ │ ├── plot_results.m % 绘图 │ └── export_result.m % 导出数据 └── main.m % 主入口按顺序调用每个模块只做一件事输入输出参数接口明确。这样做最大的好处是当你需要测试不同场景生成方法时只需要替换scenario/generate_scenarios.m一个文件其他部分完全不用动。6.2 参数配置与结果记录的固定套路用结构体集中管理参数比散落在脚本各处的变量赋值容易维护得多。我一般这样组织params struct(); params.delta_t 1; % 时间段时长小时 params.N_t 24; % 调度时段数 params.base_load base_load; % 基础负荷数据MW params.N_g 6; % 常规机组数量 params.gen struct(a, [], b, [], c, [], Pmin, [], Pmax, [], ramp, []); params.ev struct(N_agg, 1, C_cap, 500, P_max_ch, 200, P_max_dis, 200, eta_ch, 0.95, eta_dis, 0.95); params.wind_penalty 1000; % 弃风惩罚价格元/MWh params.ev_price 300; % 单位EV调度成本元/MWh每组参数跑完把目标函数值、弃风率、平均成本、求解时间全部记录到一个Excel表里标好参数组合的含义。不要嫌麻烦做敏感性分析的时候这套记录能帮你省掉大量重复跑相同参数的时间。6.3 最终复盘一篇论文复现的交付物应该包含什么如果你要把复现过程写成报告或者作为自己的项目成果至少要包含以下内容论文模型的关键公式推导或重述标注出与你实际代码对应关系数据来源表每个参数都写明出处场景生成与削减的可视化图削减前全景、削减后代表性场景调度结果图表机组出力曲线、EV充放电曲线、可再生能源消纳情况与论文结果的对比分析列出差异及原因敏感性分析如不同惩罚价格下调度行为的变化代码文件及说明文档很多同学复现完了只留一张结果图数值都能跑但其实对模型的理解很快就模糊掉了。我自己的习惯是每条公式对应代码里的具体行写成注释放在代码里以后回头查阅或者换个模型复用这些代码效率是成倍的差别。7. 几个容易忽略的细节与最后的个人心得聊几个我复现这类论文时反复被“坑”的细节每一个都很小但每一个都足以让结果彻底走偏。单位换算这事我和A同学排查过一整个下午。论文里EV电池容量写成50到底是什么单位如果默认是kWh聚合500辆车就是25MWh如果是MWh那就是25000kWh差了一千倍。这往往就是目标函数值对不上曲线形状却完全一致的原因。时段粒度的隐式假设有篇论文用了一天24点数据但EV出行高峰在正文里含糊地用“某个时段”表述复现时用用户调研数据重算了负荷分布结果与原论文图出现一小时偏移。这种偏移不是模型错而是输入数据的时间戳对不齐。求解器数值稳定性。Gurobi和Cplex在默认精度下处理数值极端的系数矩阵时可能会出现不可行判定或者虚假的“对偶不可行”提示信息。我处理的方法是把目标函数里各项的单位统一到同一数量级比如成本都用“万元”而非“元”功率都用“MW”而非“kW”避免系数从1e-6到1e6跨度太夸张。这个习惯能省下大量的调参时间。最后说一个贯穿始终的心得。复现论文最忌讳的是“跑通就行”。你代码能跑曲线能画论文图能对上这是下限。真正的收获在于搞明白每个参数为什么取那个值每个约束为什么存在每个简化为的是什么。就拿EV协同这一块来说你把模型里的电池老化约束去掉跑出来的万无一失是“EV整天充放电、系统成本更低”但你背着这个结果去答辩评委一问“电池损耗算了吗”你就知道这个细节当初是否真的理解了。我在实际做这类项目时习惯把一个算例反复改参数跑十几遍每改一个参数都记录下结果的变化方向。等到最后写总结时就能理直气壮地说我不仅复现了这篇论文还知道了它哪些地方是“设计使然”哪些地方是“作者没考虑到的简化”。希望这篇文章也能帮你达到这样的状态。