ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LINGO数学建模实战:从线性规划到整数规划,优化问题高效求解

LINGO数学建模实战:从线性规划到整数规划,优化问题高效求解 1. 项目概述为什么是LINGO如果你正在接触数学建模或者你的专业是运筹学、管理科学、工业工程那么“LINGO”这个名字你大概率绕不开。我第一次接触它是在一个供应链优化的课程项目里面对几十个决策变量和上百条约束用Excel规划求解直接卡死用MATLAB写代码又觉得调试过程过于繁琐。当时导师轻描淡写地提了一句“试试LINGO吧。”结果一个困扰我一周的线性规划模型在LINGO里只用了不到十行代码就描述清楚并且秒出最优解。那一刻的震撼让我彻底记住了这个工具。简单来说LINGO是一款专门用于求解最优化问题的软件。它的核心价值在于“描述性建模”。什么叫描述性建模我们传统的编程思维是“过程式”的你需要告诉计算机第一步做什么第二步判断什么第三步循环什么。但在解决优化问题时我们更关心的是“问题本身是什么”——我的目标是什么最大化利润还是最小化成本我有哪些限制条件资源上限、市场需求决策变量之间有什么关系LINGO让你可以直接用近乎数学公式的语言把这些“是什么”写出来至于“怎么求解”这个最复杂的部分它内置的强大求解器会帮你搞定。它就像一个数学建模领域的“翻译官”兼“解题高手”你把用文字和公式描述的问题“说”给它听它负责找到最好的答案。对于数学建模竞赛如国赛、美赛而言LINGO尤其适合解决那些具有清晰数学结构的问题比如线性规划、整数规划、非线性规划、二次规划等。它的语法简洁入门曲线相对平缓能让你把精力集中在模型构建本身而不是耗费在算法实现和调试上。当然它并非万能对于动态规划、复杂模拟等类型的问题可能不是最优选但在其擅长的领域它的效率和便捷性是无可替代的。接下来我将从一个实际使用者的角度带你从零开始拆解LINGO的核心让你不仅能“用上”更能“用好”它。2. LINGO的核心思想与建模逻辑拆解2.1 从数学公式到LINGO代码思维转换要玩转LINGO第一步是完成思维转换。我们以一个最简单的“资源分配”问题为例某工厂生产A、B两种产品生产每件A产品消耗原料甲2公斤、原料乙1公斤利润3千元生产每件B产品消耗原料甲1公斤、原料乙3公斤利润4千元。现有原料甲100公斤原料乙90公斤。问如何安排生产使总利润最大我们的数学模型是这样的决策变量设生产A产品 ( x_1 ) 件B产品 ( x_2 ) 件。目标函数最大化总利润 ( Max Z 3x_1 4x_2 )。约束条件原料甲限制( 2x_1 x_2 \leq 100 )原料乙限制( x_1 3x_2 \leq 90 )非负约束( x_1, x_2 \geq 0 )在LINGO中我们如何“描述”这个模型呢它遵循一个非常直观的“三段式”结构集合段SETS定义问题的维度。对于简单问题此段可省略直接从数据段开始。但对于中大型问题比如有10种产品、20个仓库先定义集合是保持代码清晰的关键。本例简单我们暂时不用。数据段DATA输入模型中的已知参数。比如原料上限、利润系数、消耗系数等。模型段MODEL核心部分包含目标函数和约束条件。对于上述问题一个最直接的LINGO代码如下不显式使用集合段! 目标函数; MAX 3*x1 4*x2; ! 约束条件; 2*x1 x2 100; x1 3*x2 90; ! 非负约束LINGO默认变量非负此句可省略; BND(0, x1, INF); BND(0, x2, INF);看是不是几乎和数学公式一一对应MAX定义了最大化目标表示小于等于约束。!后面是注释。这就是描述性建模的魅力你写的就是问题本身。注意LINGO默认所有变量都是连续且非负的。所以在这个例子里最后两行BND函数设定变量上下界其实可以省略。但明确写出来是个好习惯尤其是当变量有负数或上界时。2.2 集合SETS与数据DATA处理规模化问题的基石当你的问题规模变大时比如有50种产品再用x1, x2, ..., x50和一堆散落的系数来写模型将是灾难性的。这时集合SETS的概念就至关重要了。假设我们有3种产品P1, P2, P3使用2种原料M1, M2。我们可以这样定义MODEL: SETS: PRODUCT /P1, P2, P3/: Profit, X; ! 产品集合每个产品有属性“利润”和决策变量“产量X”; MATERIAL /M1, M2/: Capacity; ! 原料集合每个原料有属性“容量上限”; LINK(PRODUCT, MATERIAL): Consume; ! 派生集合表示产品与原料的关系属性为“消耗系数”; ENDSETS DATA: Profit 3, 4, 5; ! 产品利润; Capacity 100, 90; ! 原料容量; Consume 2, 1, ! P1消耗M12, M21; 1, 3, ! P2消耗M11, M23; 3, 2; ! P3消耗M13, M22; ENDDATA ! 目标函数总利润最大; MAX SUM(PRODUCT(I): Profit(I) * X(I)); ! 约束条件每种原料的消耗总量不超过其容量; FOR(MATERIAL(J): SUM(PRODUCT(I): Consume(I, J) * X(I)) Capacity(J) ); END关键点解析SETS:和ENDSETS之间定义了所有集合。PRODUCT /P1, P2, P3/: Profit, X;这行定义了名为PRODUCT的集合其成员是P1, P2, P3。冒号后面定义了该集合的属性Profit利润已知参数和X产量决策变量。属性相当于附着在每个成员身上的数据。LINK(PRODUCT, MATERIAL): Consume;定义了一个派生集合它由PRODUCT和MATERIAL组合而成代表了“哪种产品消耗哪种原料”这个关系。其属性Consume就是一个3行2列的矩阵消耗系数表。DATA:和ENDDATA之间用于给已知属性赋值。赋值顺序必须与集合中成员的顺序严格对应。SUM和FOR是LINGO最核心的集合循环函数。SUM(PRODUCT(I): ...)表示对产品集合I进行求和。FOR(MATERIAL(J): ...)表示对原料集合J中的每一个成员生成一条约束。这使得无论产品变成50种还是100种目标函数和约束的代码都无需改变只需在数据段更新集合成员和参数即可。这是LINGO处理大规模问题的精髓所在。实操心得在构建复杂模型时花时间设计好集合结构是事半功倍的第一步。清晰的集合定义能让你的模型逻辑一目了然后期调试和修改数据也会非常方便。我个人的习惯是在写代码前先用纸笔画一下模型中的“实体”如工厂、客户、产品、时间段以及它们之间的“关系”这些通常就对应着基本集合和派生集合。3. LINGO语法精要与关键函数详解掌握了集合思想我们来深入看看LINGO的语法细节和那些能让你如虎添翼的函数。3.1 基本语法规则与常见运算符语句结束每个语句以分号;结束。这是必须的。注释感叹号!之后到行尾的内容为注释不会被求解器执行。多行注释可以用!开始但更常见的做法是每行都用!。变量名以字母开头由字母、数字和下划线组成不区分大小写。避免使用LINGO保留字如MAX,MIN,SUM,FOR,END等。运算符算术运算符,-,*,/,^幂。关系运算符在LINGO中单个等号表示等式约束小于大于小于等于大于等于。注意LINGO没有单独的“赋值”概念所有等号在模型段都是约束。逻辑运算符主要用于整数规划#AND#, #OR#, #NOT#, #EQ#, #NE#, #GT#, #GE#, #LT#, #LE#。这些在定义条件约束时非常有用。3.2 核心函数库从求和到逻辑判断LINGO内置了丰富的函数这里分类介绍最常用的1. 集合循环函数建模骨架SUM(set[(index)[|condition]]: expression)求和。|condition是可选的过滤条件。例SUM(WAREHOUSE(J) | Capacity(J) 50: Cost(J))只对容量大于50的仓库的成本求和。FOR(set[(index)[|condition]]: constraint_expressions)生成循环约束。可以为集合中每个或满足条件的成员生成一条约束。MAX,MIN求集合中表达式的最大值/最小值。2. 变量界定函数定义变量类型这是将模型从线性规划扩展到整数规划、0-1规划的关键。GIN(variable)限制变量为非负整数。例如GIN(X)表示X是整数。BIN(variable)限制变量为0-1变量二进制变量。例如BIN(Y)表示Y只能是0或1。这在选择、指派、固定成本问题中必不可少。BND(lower, variable, upper)给变量设定上下界。比直接用和约束更高效。例如BND(5, X, 20)。FREE(variable)取消变量的非负限制允许其为任意实数可正可负。LINGO默认变量非负所以当你的模型允许产量为负比如允许退货超过生产时必须用此函数声明。3. 数学与金融函数ABS(x),EXP(x),LOG(x),SIN(x),COS(x)等标准数学函数。SMAX(x1, x2, ...),SMIN(...)返回参数列表中的最大值/最小值。注意与MAX/MIN针对集合的区别。PBN(p, n, x)二项分布累积概率。PSN(x)标准正态分布累积概率。在随机规划或包含概率约束的模型中会用到。4. 文件输入输出函数FILE(filename)从外部文本文件读取数据。对于大型数据如一个100*100的矩阵直接在DATA段写不现实用此函数从data.txt读取是标准做法。TEXT(filename)将计算结果输出到文本文件。OLE(Excel文件路径, 范围名称)极其重要直接从Excel工作表读取数据或将结果写回Excel。这大大简化了数据准备工作。你可以在Excel中维护你的参数表在LINGO中通过OLE函数关联修改数据只需在Excel中进行无需改动LINGO模型文件。注意事项使用GIN或BIN将模型变为整数规划后求解时间可能会急剧增加尤其是变量较多时。在建模时要仔细思考是否每个变量都必须为整数。有时可以先求解线性松弛问题去掉整数限制得到一个最优解的下界对于最大化问题或上界对于最小化问题作为参考再求解整数规划。4. 完整建模实战一个生产计划问题我们通过一个更综合的例子将上述知识串联起来。问题某公司生产两种产品I和II需要经过A、B两道工序。数据如下表产品工序A耗时小时/件工序B耗时小时/件原材料成本元/件产品售价元/件I21530II12420每周可用工时工序A为100小时工序B为80小时。市场调查表明产品I每周最多可销售40件。此外公司决定至少生产10件产品I。如果生产产品II则需要启动一条专用生产线产生固定成本200元即只要生产II无论多少件都需支付200元。目标是制定周生产计划使每周净收益最大。建模思路分析决策变量设产品I产量为X1产品II产量为X2。此外由于生产产品II有固定成本我们需要一个0-1变量Y来表示是否生产产品II。Y1表示生产Y0表示不生产。目标函数净收益 销售收入 - 原材料成本 - 固定成本。销售收入30*X1 20*X2原材料成本5*X1 4*X2固定成本200*Y所以MAX (30-5)*X1 (20-4)*X2 - 200*Y;即MAX 25*X1 16*X2 - 200*Y;约束条件工时约束2*X1 X2 100(A工序)X1 2*X2 80(B工序)。销量上限X1 40。最低产量X1 10。关键约束——固定成本与产量的逻辑关系如果Y0不生产II则X2必须为0如果Y1生产II则X2可以大于0但理论上应有一个上限比如不超过市场最大需求这里我们先不设上限仅建立逻辑关系。这种“如果...则...”的关系需要用到一个大M法来建模。我们引入一个很大的正数M例如取一个比X2可能的最大值还要大的数这里根据工时约束X2最大不会超过80取M100足够。约束可以写为X2 M * Y。解释当Y0时不等式右边为0因此X2 0结合非负约束得到X20。当Y1时不等式右边为M这是一个很大的数相当于对X2没有实际限制只要不超过M就行。变量类型X1, X2 0且连续Y为0-1变量。LINGO代码实现MODEL: ! 定义参数; M 100; ! 大M; ! 决策变量; X1 0; ! 产品I产量连续变量; X2 0; ! 产品II产量连续变量; Y 0; ! 是否生产产品II0-1变量; ! 目标函数最大化净收益; MAX 25*X1 16*X2 - 200*Y; ! 约束条件; ! 工时约束; 2*X1 X2 100; ! 工序A; X1 2*X2 80; ! 工序B; ! 销量与产量约束; X1 40; X1 10; ! 固定成本逻辑约束大M法; X2 M * Y; ! 变量类型定义; BIN(Y); ! Y是0-1变量; ! X1, X2默认非负连续无需特别声明; END求解与结果解读 点击LINGO的“Solve”按钮求解。求解完成后会弹出状态窗口。查看报告你会看到Variable部分X1和X2的最优值以及Y的值0或1。Value列对应变量的最优解。Reduced Cost列缩减成本在线性规划中意义重大表示该变量目标函数系数需要改善多少才能进入最优解基。对于整数规划解释更复杂通常关注值即可。Row部分对应约束的松弛/剩余变量Slack or Surplus和对偶价格Dual Price。松弛变量表示约束的“富裕”程度对偶价格表示该约束右端项增加一个单位时目标函数值的改善量对于最大化问题是增加量最小化问题是减少量这是非常宝贵的灵敏度分析信息。在这个模型中求解后你可能会发现Y1X2为一个正数这意味着生产产品II并支付固定成本是划算的。你可以尝试修改产品II的售价或固定成本观察Y和X2的变化这就是模型的分析能力。5. 求解器选择、结果分析与调试技巧5.1 求解器简介与选择LINGO内置了多个求解器针对不同类型的问题线性求解器求解线性规划LP问题。这是最快、最稳定的。非线性求解器求解非线性规划NLP问题。又分为局部求解器和全局求解器。局部求解器可能找到局部最优解而全局求解器Global Solver会尝试寻找全局最优解但耗时可能很长。整数求解器当模型中包含GIN或BIN变量时LINGO会自动调用整数求解器。它通常在线性规划松弛解的基础上采用分支定界法等算法寻找整数最优解。如何选择通常你无需手动选择LINGO会根据你的模型自动识别并调用合适的求解器。但你需要知道如果你的模型本质是线性的确保你写成了线性形式避免x*y,x/y,LOG(x)等非线性项这样才能享受线性求解器的高速。如果你确实有非线性项并想寻找全局最优解需要在LINGO - Options - Global Solver中勾选Use Global Solver。对于复杂非线性问题求解前设置合理的变量初始值INIT有时能帮助求解器更快找到好解。5.2 读懂求解报告与灵敏度分析求解报告是理解模型结果的金矿。除了最优解请特别关注状态StateGlobal Optimum全局最优是最理想的。Local Optimum局部最优在非线性问题中可能出现。Infeasible不可行意味着约束条件互相矛盾无解。Unbounded无界意味着目标函数值可以无限增大对于Max问题或减小对于Min问题通常是因为缺少必要的约束。松弛变量Slack or Surplus对于约束松弛变量表示“还剩多少资源没用完”。对于约束剩余变量表示“超过最低要求多少”。如果松弛/剩余变量为0则该约束是紧的Binding即该资源刚好用完或要求刚好满足再改变其限制会直接影响最优解。对偶价格Dual Price这是灵敏度分析的核心。它告诉你某个约束的右端常数比如原料上限100小时增加一个微小单位时目标函数最优值会改善多少。例如如果工序A工时的对偶价格是5那么增加1小时可用工时总利润预计能增加5元。这为管理决策如是否购买额外机器工时提供了量化依据。5.3 常见错误与调试心得No feasible solution found找不到可行解原因约束条件过于严格互相冲突。排查逐一检查每个约束的逻辑。一个常用技巧是先注释掉所有约束让模型能运行并得到一个解可能无意义。然后逐个恢复约束每恢复一个就求解一次直到找到导致不可行的那个约束。检查该约束的常数项和系数是否输入错误。Unbounded solution无界解原因目标函数可以无限优化通常是因为忘记了对决策变量施加必要的上限约束或者目标函数系数符号有误。排查检查目标函数是Max还是Min系数是否正确。对于代表数量的变量思考其是否有现实意义上的上限如市场容量、生产能力并加上相应的约束。求解时间过长特别是整数规划对策尝试为整数变量提供好的初始值如果凭经验能估计的话。在LINGO - Options - Integer Solver中调整Solver设置如将“分支方向”从默认的“Both”改为“Up”或“Down”。设置一个合理的“最优间隙容差”Optimality Tolerance。默认是0意味着必须找到绝对最优解。你可以将其设为一个小数如0.01这样当LINGO找到一个解且证明其与最优解的差距在1%以内时就可能停止搜索大大缩短时间。审视模型看是否有一些整数变量其实可以放松为连续变量而不影响实际问题。语法错误LINGO的语法检查比较直观。错误信息会定位到行。常见错误有缺少分号;、集合定义与数据赋值不匹配、使用了未定义的集合或属性、括号不匹配等。仔细阅读错误提示通常能快速定位。我的调试习惯对于任何一个新建立的复杂模型我绝不会一次性写完所有代码。我会采用“增量建模”法先构建一个极度简化的版本比如只有核心约束和变量确保它能运行并得到合理结果。然后像搭积木一样逐步添加更复杂的约束如逻辑约束、非线性项。每添加一部分就运行一次确认模型状态正常。这样当错误出现时你很容易就知道是刚刚添加的哪个部分引起的。另外多用WRITE函数在求解前输出关键参数值或在求解后输出中间计算结果这是跟踪模型内部状态的利器。
返回列表