ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

GAMS二次开发教程(08):第一个优化实战——运输问题的求解、敏感性与报表输出

GAMS二次开发教程(08):第一个优化实战——运输问题的求解、敏感性与报表输出 GAMS二次开发教程08第一个优化实战——运输问题的求解、敏感性与报表输出版本与许可声明工具/软件GAMS版本号以官方发行说明为准。模型与数据取自官方 Rosenthal 教程的运输问题示例trnsport数据改编自 Dantzig, 1963报表机制取自官方 “The Put Writing Facility” 章落盘机制取自 “GAMS Data eXchange (GDX)” 章。语言/环境GAMS 语言纯文本.gms示例为 LP。本文目标把“模型跑通了”升级为“结果可验算、可解释、可交付、可被下游消费”。许可边界求解需要有效 GAMS 许可含官方 demo license其规模上限以官方许可说明为准。本篇所有数值均取自官方教程示例输出未做任何推测。一句话结论trnsport的最优解是一组确定的数字——目标值153.675千美元四个非零运量为 Seattle→New York50、Seattle→Chicago300、San Diego→New York275、San Diego→Topeka275两个非零变量边际值为x.m(seattle,topeka) 0.036、x.m(san-diego,chicago) 0.009千美元/箱把这组数字写成“按厂汇总 / 按市场汇总 / 目标值”三个可断言的量放进Display用put输出规范报表、用execute_unload输出 GDX你就得到一个每次改动都能自证没改坏语义的最小交付流程。〇、本篇要解决的认知问题Q1把trnsport做成“可交付流程”要哪几步求解、读解、报表各由谁负责边界在哪Q2怎样把.l的解组织成“按厂 / 按市场”的结构化汇总并一眼验算可行性与守恒Q3.m边际值在运输问题里读的是什么影子价格与供需松弛的经济含义怎么解释Q4用display与用put生成报表的取舍是什么put的光标定位与字段格式怎么用Q5怎样给求解加上边界optCR/iterLim/resLim并把解落成 GDX 交给 Excel/Python一、机制解析1.1 “可交付流程”的五步与四层第 03 篇把trnsport当“教学样例”拆过一遍本篇把它当“交付物”重做一遍。差别不在模型而在流程。可交付流程固定是五步① 数据层 集合 / 参数 / 表格 → 输入数据一次录入、可复现 ② 模型层 变量 / 方程 / Model 装配 → 方程代数与业务口径一一对应 ③ 求解层 Solve 选项边界 → 有类型、有方向、有上限、有状态判读 ④ 读解层 .l / .m → 汇总参数 → 把“解的性质”变成可断言的量 ⑤ 报表层 display / put / execute_unload → 人看 .lst 与 .txt机器看 GDX每一层的边界必须干净数据层不做逻辑求解层不做汇总读解层不碰原始数据报表层不做计算。这条纪律的直接收益是——当结果异常时你能立刻知道该去哪一层找问题数据不对去①约束写错去②状态不是 1 去③汇总对不上输入去④格式乱去⑤。铁律 8改完必验trnsport是这个系列的“门禁模型”因为它的最优解是公开且确定的目标 153.675、四个运量 50/300/275/275、两个边际值 0.036/0.009。任何一次二次开发换数据源、换装配方式、加报表、加落盘之后都先跑它这组数字成立才轮到谈别的模型。1.2 读解三件套.l、汇总参数、守恒检查官方 “Variables” 章对两个后缀的定义极其精确.l是 activity level活动水平求解后被刷新.m是 marginal value边际值/影子价格求解后被刷新。光看.l不够因为解是否可行、是否守恒光看原始解是看不出来的。标准做法是把“解的性质”写成参数再和“输入的性质”放进同一个Display探针写法断言按厂汇总used(i) sum(j, x.l(i,j)) ;应等于a(i)LP 最优时产能用满按市场汇总served(j) sum(i, x.l(i,j)) ;应等于b(j)所有单位运费为正故不会超运目标值zOpt z.l ;应为 153.675这三个探针的意义是“把验算变成可执行的”。人工眼看六个数很容易放过错误把三个汇总参数与输入量并排Display错误会自己跳出来。这也正是第 03 篇提到的used/served技巧的完整化。1.3 敏感性.m读的是什么运输问题是最经典的“影子价格教科书”。它的边际值有两类各读不同的经济含义变量.mreduced cost读的是“硬要让一个取 0 的通道运一箱总成本会涨多少”。官方教程给出的两个非零值是x.m(seattle,topeka) 0.036与x.m(san-diego,chicago) 0.009千美元/箱并明确解释从 Seattle 到 Topeka 最优是运 0但硬要运一箱会让最优成本增加 0.036 千美元36 美元。这个数不是“运费”而是“在这个最优方案下这条通道值不值得重新谈判”的定价。方程.m约束的影子价格supply.m(i)读的是“产能a(i)放宽一箱能省多少”demand.m(j)读的是“需求b(j)刚性提高一箱要多花多少”。它们是 LP 对偶变量是把“资源松一点值多少钱”量化出来的东西。读法上有一条纪律.m的数字伴随那个特定的最优基存在一旦数据变了基可能换影子价格可能整段失效LP 对偶解不唯一时尤其如此。所以敏感性分析的规范做法不是“把.m抄出来讲故事”而是改变一个数据、重新求解、比较目标值——本篇的代码 8-3 就是这么做的。官方教程输出里只给出了x.m的两个非零值supply.m/demand.m的数值以你本次运行的.lst为准本文不代为罗列。铁律 10数值不臆造影子价格、对偶变量、敏感度这类数最容易被“顺手编一个”。凡本文未从官方教程或本次运行输出中取得的具体数值一律不写需要时以Display supply.m, demand.m或.lst的输出为准。1.4 报表双轨display与put官方对两者的定位划得很清楚display是“把数据写进 listing 文件的快捷方式”追求易用与合理默认put是“生成外部 ASCII 文件”的完整报表设施可控制三层格式外部文件、页面、输出项但需要更多编程。维度displayput输出去向.lstlisting外部 ASCII 文件默认扩展名.put可显式命名定位能力无自动排版有定位列、#定位行官方例1#6 第 1 列第 6 行字段格式全局option decimals/dispWidth局部option ident:d:r:c局部写法item:宽度:小数位官方例x.l(i,j):8:4适用场景调试、快速核对、回归断言交付报表、给外部系统看的固定格式文本put的三个关键语法点均来自官方首例File语句定义文件File factors /factors.dat/, results /results.dat/ ;——内部名供模型引用/…/里是外部真实文件名省略外部名时默认生成“内部名 .put”。put file_name ;切换当前文件put item, item, … ;写内容putclose file_name ;关闭。斜杠表示“移到下一行第 1 列”两个斜杠就是插入空行——但官方专门警告//是常见的行为内注释符所以两个斜杠之间务必留一个空格。1.5 求解边界与结果落盘给求解加边界用的是option语句第 11 篇会展开三层优先级option resLim 100 ;——求解器墙钟时间上限秒option iterLim 10000 ;——求解器迭代上限option optCR 0.01 ;——相对最优性容差。官方把它归在“求解器相关参数”里要诚实地说清它的作用域它是离散模型MIP 类的最优性判据对纯 LP 一般不起作用——LP 要么解到最优要么被时间/迭代上限截断。落盘用execute_unload file.gdx, sym1, sym2 ;把内存里的符号写进 GDX。选择它而不是GDXXRW的理由是一条平台纪律铁律 5数据交换分平台GDXXRW仅 Windows 可用官方 T_GDXXRW 页原文GDXXRW is available on Windows only并特别提醒迁移到 GAMS Engine 时要注意这一点而GDX 是三平台通用的二进制交换格式。因此“模型层→下游”的接口应当用 GDXExcel 读写属于另一条旁路见第 10 篇。二、完整代码与逐行剖析代码 8-1ch08_trnsport_report.gms—— 求解 读解 验算 落盘完整可运行$title ch08_trnsport_report - solve, verify, and unload the transport model * 适用分发口径模型与求解以官方 Model and Solve Statements 页为准数据取自官方 Rosenthal 教程 * 求解许可SOLVE using lp需要有效 GAMS 许可含 demo license * 平台GAMS 语句通用落盘用 GDX三平台通用不使用仅 Windows 的 GDXXRW Set i canning plants / seattle, san-diego / j markets / new-york, chicago, topeka / ; Parameter a(i) capacity of plant i in cases / seattle 350, san-diego 600 / b(j) demand at market j in cases / new-york 325, chicago 300, topeka 275 / ; Table d(i,j) distance in thousands of miles new-york chicago topeka seattle 2.5 1.7 1.8 san-diego 2.5 1.8 1.4 ; Scalar f freight in dollars per case per thousand miles / 90 / ; Parameter c(i,j) transport cost in thousands of dollars per case ; c(i,j) f * d(i,j) / 1000 ; Variables x(i,j) shipment quantities in cases z total transportation costs in thousands of dollars ; Positive Variable x ; Equations cost define objective function supply(i) observe supply limit at plant i demand(j) satisfy demand at market j ; cost.. z e sum((i,j), c(i,j)*x(i,j)) ; supply(i).. sum(j, x(i,j)) l a(i) ; demand(j).. sum(i, x(i,j)) g b(j) ; Model transport / all / ; * ---- 求解边界时间与迭代上限optCR 是离散模型的最优性容差 ---- option resLim 100 ; option iterLim 10000 ; option optCR 0.01 ; Solve transport using lp minimizing z ; * ---- 读解三件套把解的性质写成参数与输入的性质并排对照 ---- Parameter used(i) capacity actually used at plant i (cases) served(j) demand actually served at market j (cases) ; used(i) sum(j, x.l(i,j)) ; served(j) sum(i, x.l(i,j)) ; Scalar zOpt optimal objective of the base model (k usd) gapUp unused capacity, must be zero at this optimum gapDn over-served demand, must be zero at this optimum ; zOpt z.l ; gapUp sum(i, a(i) - used(i)) ; gapDn sum(j, served(j) - b(j)) ; * ---- 报表状态 目标 两个守恒探针 解 边际值 ---- Display transport.solvestat, transport.modelstat, zOpt, gapUp, gapDn ; Display used, served ; Display x.l, x.m ; * ---- 落盘层间接口。GDX 是三平台通用的二进制格式 ---- execute_unload ch08_sol.gdx, x, z, c, a, b, used, served ;逐行剖析option resLim/iterLim/optCR三行是求解边界。它们是 GAMS 选项官方option语句的“求解器相关参数”组在求解前生效。optCR对本例这种纯 LP 通常不起作用——这一点必须诚实说明否则读者会以为“设了 optCR 就会更快”。used(i) sum(j, x.l(i,j)) ;与served(j) sum(i, x.l(i,j)) ;是读解层的核心。它们把.l从“六个数”变成“两个可对照表和”。gapUp sum(i, a(i) - used(i)) ;与gapDn sum(j, served(j) - b(j)) ;是守恒探针本例最优时两者都应为 0。这两个标量比“看 x.l”可靠得多——它们把“可行性”压缩成两个数。注意若模型的最优解真的用不满产能例如总产能远大于总需求gapUp会大于 0那是业务事实而不是错误所以断言要写“与预期一致”而不是死写“必须为 0”。Display transport.solvestat, transport.modelstat, ...先打状态。判读顺序solvestat 1正常完成→modelstat 1最优→ 才轮到数值。execute_unload ch08_sol.gdx, x, z, c, a, b, used, served ;刻意把c、a、b也一起导出下游要复现目标值就必须有单位运费与供需数据。这是“层间接口要自包含”的最小示范。必须背下来的验算结论官方教程输出zOpt 153.675x.l只有四组非零——(seattle,new-york) 50、(seattle,chicago) 300、(san-diego,new-york) 275、(san-diego,topeka) 275x.m只有两组非零——(seattle,topeka) 0.036、(san-diego,chicago) 0.009。代码 8-2ch08_put.gms—— 用put生成两个外部报表完整可运行$title ch08_put - external text reports with the put writing facility * 适用分发口径put 语法以官方 The Put Writing Facility 页首例为准 * 求解许可SOLVE using lp需要有效 GAMS 许可 * 平台GAMS 语句通用输出为纯文本 ASCII 文件 Set i canning plants / seattle, san-diego / j markets / new-york, chicago, topeka / ; Parameter a(i) capacity of plant i in cases / seattle 350, san-diego 600 / b(j) demand at market j in cases / new-york 325, chicago 300, topeka 275 / ; Table d(i,j) distance in thousands of miles new-york chicago topeka seattle 2.5 1.7 1.8 san-diego 2.5 1.8 1.4 ; Scalar f freight in dollars per case per thousand miles / 90 / ; Parameter c(i,j) transport cost in thousands of dollars per case ; c(i,j) f * d(i,j) / 1000 ; Variables x(i,j) shipment quantities in cases z total transportation costs in thousands of dollars ; Positive Variable x ; Equations cost, supply(i), demand(j) ; cost.. z e sum((i,j), c(i,j)*x(i,j)) ; supply(i).. sum(j, x(i,j)) l a(i) ; demand(j).. sum(i, x(i,j)) g b(j) ; Model transport / all / ; option limRow 0; option limCol 0; Solve transport using lp minimizing z ; * ---- 定义两个 put 文件内部名 - 外部文件名 ---- File factors /ch08_factors.dat/, results /ch08_results.dat/ ; * ---- 报表一输入侧因子 ---- put factors ; put Transportation Model Factors / / Freight cost , f, 1#6, Plant capacity / ; loop(i, put 3, i.tl, 15, a(i) / ) ; put / Market demand / ; loop(j, put 3, j.tl, 15, b(j) / ) ; * ---- 报表二结果侧运量字段宽 8、小数 4 位 ---- put results ; put Transportation Model Results / / ; loop((i,j), put i.tl, 12, j.tl, 24, x.l(i,j):8:4 / ) ; * ---- 关闭当前 put 文件确保缓冲落盘 ---- putclose results ;逐行剖析File factors /ch08_factors.dat/, results /ch08_results.dat/ ;一条File语句定义两个文件。官方说明外部名可省略省略时生成“内部名 .put”这里显式命名是为了让交付物名字可控。put Transportation Model Factors / / Freight cost , f, 1#6, Plant capacity / ;一行演示四件事写字符串、用/ /中间留空格插入一个空行、写标量f、用1#6把光标定位到第 1 列第 6 行。官方把定义为“定位到某列”、#定义为“定位到某行”。loop(i, put 3, i.tl, 15, a(i) / ) ;在循环里写报表要写集合元素必须先有一个循环官方明确put一次只能写一个索引元素i.tl取的是该标签的文本。put语句必须放在循环结构内这是 put 报表最常见的语法错误来源。loop((i,j), put i.tl, 12, j.tl, 24, x.l(i,j):8:4 / ) ;是双指标循环 局部字段格式:8:4宽 8、小数 4 位。官方原例就是这样写的输出形如Seattle New-York 50.0000。putclose results ;显式关闭。不关闭的后果是最后一页可能不落盘——put 设施是“按页缓冲、页满才写”的官方说明当前页存在缓冲里页满时自动写入外部文件所以putclose不是可选项。全段把option limRow 0; option limCol 0;放在Solve之前让.lst干净到只剩解报告报表的固定格式交给.dat文件。这是“人看文本、机看 GDX、调试看 lst”三分工的典型配置。代码 8-3ch08_sensitivity.gms—— 需求上浮 10% 的敏感性重跑完整可运行$title ch08_sensitivity - re-solve with a scaled demand and compare * 适用分发口径同一模型多次求解以官方 Model and Solve Statements 页为准 * 求解许可两次 SOLVE using lp需要有效 GAMS 许可 * 平台GAMS 语句通用 Set i canning plants / seattle, san-diego / j markets / new-york, chicago, topeka / ; Parameter a(i) capacity of plant i in cases / seattle 350, san-diego 600 / b(j) demand at market j in cases / new-york 325, chicago 300, topeka 275 / ; Table d(i,j) distance in thousands of miles new-york chicago topeka seattle 2.5 1.7 1.8 san-diego 2.5 1.8 1.4 ; Scalar f freight in dollars per case per thousand miles / 90 / ; Parameter c(i,j) transport cost in thousands of dollars per case ; c(i,j) f * d(i,j) / 1000 ; Variables x(i,j) shipment quantities in cases z total transportation costs in thousands of dollars ; Positive Variable x ; Equations cost, supply(i), demand(j) ; cost.. z e sum((i,j), c(i,j)*x(i,j)) ; supply(i).. sum(j, x(i,j)) l a(i) ; demand(j).. sum(i, x(i,j)) g b(j) ; Model transport / all / ; * ---- 记住基准数据供重跑前恢复 ---- Parameter bBase(j) baseline demand (cases) ; bBase(j) b(j) ; * ---- 第一次求解基准场景 ---- Solve transport using lp minimizing z ; Scalar zBase objective at baseline demand (k usd) ; zBase z.l ; * ---- 把两个方程的影子价格读进参数数值以本次 .lst 为准---- Parameter supMarg(i) shadow price of supply limits (from supply.m) demMarg(j) shadow price of demand targets (from demand.m) ; supMarg(i) supply.m(i) ; demMarg(j) demand.m(j) ; * ---- 第二次求解需求整体上浮 10% ---- b(j) 1.1 * bBase(j) ; Solve transport using lp minimizing z ; Scalar zHigh objective at 10 percent demand (k usd) ; zHigh z.l ; Scalar zUp absolute increase in total cost (k usd) ; zUp zHigh - zBase ; Display transport.solvestat, transport.modelstat ; Display zBase, zHigh, zUp ; Display supMarg, demMarg ; Display bBase, b ;逐行剖析bBase(j) b(j) ;是基准数据的快照。重跑模型时最容易犯的错是“改完忘了恢复”导致第二次结果被第一次污染把原始值先存进另一个参数是最便宜的保险。Solve transport using lp minimizing z ;可以在同一个文件里调用多次官方专门有一节 “Programs with Several Solve Statements”。注意此时Model已经装配完毕第二次求解会按当前数据重新生成并求解不需要重新Model语句。zBase z.l ;与zHigh z.l ;分别在两次求解之后取值。顺序至关重要.l是“当前解”第二次Solve一执行z.l就被后者覆盖了所以要在两次求解之间把结果及时搬走。supMarg(i) supply.m(i) ;与demMarg(j) demand.m(j) ;演示方程属性.m可以参与赋值官方允许在赋值语句里使用变量与方程属性。这与代码 8-1 里读变量.m是同一机制只是对象换成了方程。具体数值由本次运行的.lst给出本文不做预测。b(j) 1.1 * bBase(j) ;是参数化重跑的手工版。它演示了“改变数据 → 重求解 → 收集结果”这个闭环当场景变成五个、十个时这个闭环会升级成第 09 篇的loop(scenario, …)范式。zUp zHigh - zBase ;直接给出总成本对需求整体上浮 10% 的敏感度。这是“用重求解做敏感性”的规范形态不去解释某个.m的数值而是让模型自己回答“数据变了多少、结果变了多少”。三、常见报错与排查报错 3-1MODEL STATUS 4 Infeasible求解期。现象编译执行正常Display里modelstat为 4。根因本模型最常见的两类——把supply(i)的l误写成e本实例总产能 950 箱 总需求 900 箱等式立刻不可行或把demand(j)的g写成e后又与产能冲突。解法用option limRow 20放开方程清单找标INFES的行先检查等式约束是不是多余。判据gapUp/gapDn若出现非预期的大数值就是过度约束的信号。报错 3-2模型无界modelstat为无界类。现象求解器报无界。根因把Positive Variable x ;漏掉或把supply(i)误写为g本该是产能上界却变成下界。解法检查变量类型修饰与关系符方向官方指出无界最常见的原因就是忘了把变量设成 positive。报错 3-3**** $170 Domain violation for element。现象数据行报$170。根因标签拼写与Set声明不一致Seattle写成Seatle。解法逐字核对这正是第 06 篇讲的域检查在工作。报错 3-4put报了与“索引/标签”相关的错误。现象put语句编译报错。根因在没有循环的情况下写集合元素官方明确一次只能写一个索引元素或漏写后缀如把i.tl写成i。解法把put放进loop给集合元素加上标签文本后缀。标签文本后缀的具体名称以官方 put 页为准官方示例代码用.tl。报错 3-5put输出文件里“少了最后一段”。现象文件存在但末尾若干行缺失。根因忘记putclose——put 设施按页缓冲页未满时内容停在缓冲区。解法在写完后显式putclose file_name ;并注意两个斜杠之间必须留空格/ /否则会被当成行内注释导致空行消失甚至整段被吃掉。报错 3-6execute_unload之后 GDX 里没有某个符号。现象下游读不到某个符号。根因execute_unload的符号清单里没列它导出的符号必须在命令里显式列出或该符号是.m/.l之类后缀属性——只有符号本体含变量的 level/marginal能被导出为变量的多个字段而不是任意后缀。解法把符号加进清单需要下游复现目标值时同时导出c、a、b这类输入数据。报错 3-7在 Linux/集群上调用GDXXRW失败。现象本地 Windows 能跑上 GAMS Engine 或 Linux 就报错。根因官方明确GDXXRW仅 Windows 可用并特别提醒这是迁移到 GAMS Engine 时要考虑的重要因素。解法把层间接口换成 GDX三平台通用Excel 交互另走第 10 篇的跨平台方案。四、动手练习练习 1跑通与验算运行代码 8-1。判定(a)transport.solvestat 1、transport.modelstat 1(b)zOpt 153.675©used等于a、served等于b(d)gapUp与gapDn均为 0(e)x.l只有四组非零、x.m只有两组非零数值与本文结论一致。练习 2落盘与消费运行代码 8-1 后用 GAMS Studio 的 GDX Viewer 打开ch08_sol.gdx。判定能看见x、z、c、a、b、used、served七个符号且x的记录数为 6含零值记录。能说出为什么c、a、b必须一起导出。练习 3put 报表运行代码 8-2。判定(a) 工作目录出现ch08_factors.dat与ch08_results.dat(b)ch08_factors.dat里 “Plant capacity” 出现在第 6 行第 1 列对应1#6©ch08_results.dat里每个运量有 4 位小数(d).lst里没有方程清单与列清单因为设了limRow/limCol为 0。练习 4敏感性重跑运行代码 8-3。判定(a)zBase 153.675(b)zHigh大于zBasezUp等于两者之差©b的每个元素是bBase对应元素的 1.1 倍(d) 从supMarg/demMarg的值能看出哪一个市场的需求最“贵”。要求报出你本次运行的supMarg/demMarg数值并说明它们来自.lst而非本文。练习 5回归门禁把代码 8-1 的option resLim从 100 改成 0即立刻超时观察transport.solvestat。判定能复现“非 1 的求解器状态”并说明为什么此时不应去看x.l的数值。五、小结与下一篇预告本篇把trnsport从“样例”做成了“交付流程”落成五个动作求解Solve ... using lp minimizing、读解.l与.m、汇总验算used/served/zOpt/两个 gap 探针、报表display看.lst、put出.dat、落盘execute_unload出 GDX。三条机制必须记住官方给出的最优解是 153.675 与四个运量 50/300/275/275两个非零变量边际值是 0.036 与 0.009put的三段式是File→put→putclose定位靠列/#行字段格式是item:宽:小数位optCR是离散模型的最优性容差对纯 LP 一般不起作用。带走的纪律GDXXRW仅 Windows、层间接口用 GDX铁律 5数值与影子价格一律取自本次运行输出铁律 10每次改动都用 153.675 这组数字回归铁律 8求解边界与许可边界要说清铁律 1、铁律 2。第 09 篇《条件表达式与流程控制》会把本篇的“手工敏感性重跑”升级为参数化重求解范式先讲透$条件写在赋值左侧、右侧、sum指标后三处的语义差异再讲执行期流程控制if/elseif/else、loop、while、for的语法与其硬限制流程控制语句内只允许执行语句禁止声明与方程定义然后是编译期$if/$ifthen/$set与执行期的分界、abort$cond的提前退出最后落到Set s /s1*s5/; loop(s, 需求基准*k(s); solve m ...; res(s)z.l;)这个多场景范式以及循环内读写 GDX 的注意事项。本篇认知问题回显FAQQ1把trnsport做成“可交付流程”要哪几步求解、读解、报表各由谁负责边界在哪A五步四层。① 数据层集合、参数、表格保证输入一次录入、可复现② 模型层变量、方程、Model装配保证代数与业务口径一一对应③ 求解层Solve加option边界并判读状态④ 读解层把.l/.m汇总成参数⑤ 报表层display写 listing、put写外部 ASCII、execute_unload写 GDX。边界纪律是数据层不做逻辑、求解层不做汇总、读解层不碰原始数据、报表层不做计算收益是结果异常时能立刻定位到某一层数据不对去①、约束写错去②、状态非 1 去③、汇总对不上输入去④、格式乱去⑤。求解由求解器负责读解由 GAMS 执行期的赋值语句负责报表由display/put/execute_unload三条通道分别负责。Q2怎样把.l的解组织成“按厂 / 按市场”的结构化汇总并一眼验算可行性与守恒A把“解的性质”写成参数再和“输入的性质”放进同一个Display。三件套是按厂汇总used(i) sum(j, x.l(i,j))、按市场汇总served(j) sum(i, x.l(i,j))、目标值zOpt z.l再加两个守恒探针gapUp sum(i, a(i) - used(i))与gapDn sum(j, served(j) - b(j))把“可行性”压缩成两个标量。本例最优时used等于a、served等于b、两个 gap 均为 0、zOpt为 153.675。注意断言要写“与预期一致”若总产能远大于总需求gapUp大于 0 是业务事实而非错误。Q3.m边际值在运输问题里读的是什么影子价格与供需松弛的经济含义怎么解释A两类。变量的.m是 reduced cost读“硬要让一个取 0 的通道运一箱总成本会涨多少”官方教程给出x.m(seattle,topeka) 0.036与x.m(san-diego,chicago) 0.009千美元/箱并解释Seattle→Topeka 最优运量为 0但硬运一箱会让最优成本增加 0.036 千美元36 美元。方程的.m是约束的影子价格supply.m(i)读“产能放宽一箱能省多少”demand.m(j)读“需求刚性提高一箱要多花多少”二者是 LP 的对偶变量。读法纪律影子价格伴随特定的最优基存在数据一变基可能换、对偶解可能不唯一所以规范做法是“改数据、重求解、比目标”而不是把.m抄出来讲故事本文不代列supply.m/demand.m的具体数值以本次运行的.lst为准。Q4用display与用put生成报表的取舍是什么put的光标定位与字段格式怎么用Adisplay是把数据写进 listing 文件的快捷方式追求易用与合理默认适合调试、快速核对与回归断言put是生成外部 ASCII 文件的完整报表设施可控制外部文件、页面、输出项三层格式适合交付报表与固定格式文本但需要更多编程。put用File name /外部文件名/ ;定义文件省略外部名时默认“内部名 .put”用put file_name ;切换当前文件、put item, … ;写内容、putclose file_name ;关闭。光标定位靠列与#行官方例1#6表示第 1 列第 6 行字段格式是局部写法item:宽度:小数位官方例x.l(i,j):8:4。三条硬注意事项集合元素必须放在loop里写、元素文本要加标签后缀、两个斜杠之间必须留空格/ /以免被当成行内注释。Q5怎样给求解加上边界optCR/iterLim/resLim并把解落成 GDX 交给 Excel/PythonA用option语句在求解前设置option resLim 100墙钟时间上限秒、option iterLim 10000迭代上限、option optCR 0.01相对最优性容差属于离散模型的最优性判据对纯 LP 一般不起作用——若求解器状态不是 1就不应去看.l的数值。落盘用execute_unload file.gdx, 符号清单 ;符号必须在清单里显式列出为让下游能复现目标值应把c、a、b这类输入数据一并导出。选择 GDX 而不是GDXXRW的原因是平台纪律GDXXRW仅 Windows 可用官方特别提醒这是迁移到 GAMS Engine 时要考虑的重要因素而 GDX 是三平台通用的二进制交换格式因此“模型层到下游”的接口一律用 GDXExcel 读写是另一条旁路第 10 篇。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进