1. 项目概述用LINGO求解经典规划问题最近在整理资料时翻到了2003年数学建模国赛的B题题目是关于“露天矿生产的车辆安排”问题。这可以说是一个经典的线性/整数规划问题也是很多数模人接触运筹优化的入门案例。当年我们团队用的是LINGO 10.0来求解虽然现在新版本层出不穷各种Python库如PuLP, Gurobi也功能强大但LINGO以其近乎“自然语言”的建模方式和高效的求解引擎在特定场景下依然有其独特的魅力。今天我就以这道题为例带大家完整走一遍用LINGO 10建立模型、编写代码、求解和分析结果的全过程。无论你是正在备战数模的新手还是想重温经典算法的老手这篇基于实战的详细拆解希望能给你带来一些直接的参考价值。这道题的核心是优化在一个露天矿里有多个铲位矿石来源和多个卸点矿石去向以及若干辆卡车。已知铲位的矿石储量、品位卸点的矿石需求量和品位要求以及卡车在不同路线上的运输时间和运输能力。目标是如何安排卡车的运输路线即从哪个铲位运到哪个卸点派多少车运多少趟才能在满足所有生产约束如产量、需求、品位均衡的前提下使得总运量最小或者总运输成本最低具体看题目要求。这本质上是一个复杂的车辆调度与资源分配问题非常适合用线性规划LP或整数规划IP来建模而LINGO正是解决这类问题的利器。2. 问题核心与模型抽象2.1 题目关键信息提取与理解2003年B题“露天矿生产的车辆安排”是一个典型的带有复杂约束的生产调度问题。要把它变成LINGO能懂的模型第一步是当好“翻译官”把文字描述转化为数学语言。首先我们需要明确所有的“元素”铲位 (Loading Sites)记为i共有多个。每个铲位有已知的矿石储量和岩石储量以及对应的铁品位。这是“供应方”。卸点 (Unloading Sites)记为j包括矿石卸点和岩石卸点。每个卸点有产量要求每天需要卸下的吨数对于矿石卸点还有品位限制要求卸入矿石的平均铁品位在一定范围内。这是“需求方”。卡车 (Trucks)是运输工具。题目给出了卡车的数量和载重量。关键的是卡车在从铲位i到卸点j的路线上的运行周期时间包括装、运、卸、返回是已知的这直接决定了运输效率。时间以班次为单位一个班次通常为8小时480分钟。这是所有调度必须遵守的时间上限。其次要理解题目的核心要求这决定了我们的目标函数和约束条件产量满足所有卸点接收的总量必须满足其产量要求。资源限制从每个铲位运出的矿石和岩石总量不能超过其储量。品位均衡对于矿石卸点所有运入的矿石混合后的平均品位必须在指定范围内。这是最容易出错的一个约束它使得问题不再是简单的运输问题而变成了一个带有“比例”约束的规划问题。卡车调度所有卡车在一个班次内的总工作时间不能超过480分钟。这里通常需要引入“卡车数量”或“运输趟数”作为变量并关联到运行时间。目标通常有两个层面。一是总运量最小吨公里数这更经济二是出动卡车数最少或总运输时间最短这更高效。有时题目会要求多目标优化或分层优化。2.2 模型建立从自然语言到LINGO代码基于以上理解我们可以开始构建数学模型。这是将问题“公式化”的关键一步。定义决策变量 这是模型的灵魂。对于本题最直接的变量定义方式是x(i,j)从铲位i到卸点j的运输趟数整数变量。 或者更精细一点可以定义为w(i,j)从铲位i运往卸点j的货物吨数。两者可以通过卡车载重量进行转换。我们这里采用“趟数”变量因为它更直观地与卡车和时间关联。目标函数 以“总运量最小”为例。总运量 Σ Σ (从i到j的趟数x(i,j)* 卡车载重量 * 从i到j的距离)。距离信息通常隐含在运行周期时间中。在LINGO中我们可以直接用一个集合循环求和来表达。约束条件 这是模型最复杂的部分需要逐一精确翻译。卸点需求约束对于每个卸点j所有铲位i运往j的吨数之和 该卸点的产量要求。sum(铲位(i): x(i,j) * 载重量) 需求(j);铲位供应约束对于每个铲位i运往所有卸点j的矿石吨数 该铲位矿石储量运往所有卸点j的岩石吨数 该铲位岩石储量。这里需要区分卸点是矿石卸点还是岩石卸点。品位约束核心难点对于每个矿石卸点j运入矿石的平均品位需在[TL, TU]之间。 平均品位 (Σ (从i运往j的矿石吨数 * i铲位的品位)) / (Σ 从i运往j的矿石吨数)。 这个等式是非线性的变量除以变量。为了在LINGO中线性化处理我们将其转化为两个线性不等式Σ (x(i,j) * 载重量 * (品位(i) - TU)) 0Σ (x(i,j) * 载重量 * (TL - 品位(i))) 0这样就把复杂的比例关系变成了加权和的形式符合线性规划的要求。这是本题建模的第一个关键技巧。卡车时间约束所有卡车在班次内总运行时间不超过480分钟 * 卡车数量。 总时间 Σ Σ (x(i,j) * 从i到j的运行周期)。这里隐含了一个假设每趟运输是独立的且卡车利用率达到理想状态。更复杂的模型会引入卡车分配变量但本题通常用此简化约束即可。整数与非负约束x(i,j)为非负整数。注意在实际竞赛中评委非常看重模型的“可解性”。将非线性品位约束线性化是保证LINGO能快速求出全局最优解的关键。如果直接使用除法形式LINGO可能会调用非线性求解器求解速度慢且可能陷入局部最优。3. LINGO 10 求解实战与代码详解模型建立后接下来就是将它“告诉”LINGO。LINGO 10的语法清晰接近数学公式。3.1 LINGO模型文件编写打开LINGO 10新建一个文件.lg4。代码主要分为几个部分集合段、数据段、目标与约束段。! 2003年国赛B题露天矿车辆安排模型 ! 定义集合 SETS: loading_site /1..10/: ore_reserve, rock_reserve, grade; ! 铲位矿石储量岩石储量品位 unloading_site /1..5/: demand, type; ! 卸点需求吨数类型(1矿石/2岩石) link(loading_site, unloading_site): time, x; ! 关系运输时间(分钟)决策变量x(趟数) ENDSETS ! 输入数据 DATA: ! 铲位数据矿石储量(吨)岩石储量(吨)品位(%) ore_reserve, rock_reserve, grade 10000 5000 30 8000 6000 45 ... (此处填入所有铲位数据) ; ! 卸点数据需求(吨)类型(1矿石, 2岩石) demand, type 30000 1 15000 1 20000 2 ... (填入所有卸点数据) ; ! 运输时间矩阵(分钟)行是铲位列是卸点 time 15 20 25 18 22 18 16 24 19 21 ... (填入完整的时间矩阵) ; ! 常量卡车载重(吨)班次时间(分钟)卡车总数品位上下限 truck_capacity 154; shift_time 480; num_trucks 20; grade_lower 30; grade_upper 35; ENDDATA ! 目标函数最小化总运输吨公里数这里简化为最小化总运输时间因距离与时间成正比 MIN SUM(link(i,j): x(i,j) * time(i,j)) * truck_capacity; ! 约束条件 ! 1. 卸点需求约束 FOR(unloading_site(j): SUM(loading_site(i): x(i,j) * truck_capacity) demand(j) ); ! 2. 铲位供应约束需区分矿石和岩石 FOR(loading_site(i): ! 运往矿石卸点(type1)的总吨数 矿石储量 SUM(unloading_site(j) | type(j) #EQ# 1: x(i,j) * truck_capacity) ore_reserve(i); ! 运往岩石卸点(type2)的总吨数 岩石储量 SUM(unloading_site(j) | type(j) #EQ# 2: x(i,j) * truck_capacity) rock_reserve(i); ); ! 3. 矿石卸点品位约束线性化处理 FOR(unloading_site(j) | type(j) #EQ# 1: ! 品位上限约束 SUM(loading_site(i): x(i,j) * truck_capacity * (grade(i) - grade_upper)) 0; ! 品位下限约束 SUM(loading_site(i): x(i,j) * truck_capacity * (grade_lower - grade(i))) 0; ); ! 4. 卡车总时间约束 SUM(link(i,j): x(i,j) * time(i,j)) shift_time * num_trucks; ! 5. 整数约束 FOR(link(i,j): GIN(x(i,j)));代码要点解析集合定义link(loading_site, unloading_site)定义了铲位和卸点之间的所有可能路径这是定义二维决策变量x和参数time的基础。条件求和在铲位供应约束中使用了| type(j) #EQ# 1这样的过滤器表示只对类型为1矿石的卸点进行求和。这是LINGO中非常强大的功能。品位约束线性化正如前面所讲我们将平均品位约束转化为了两个关于加权和的线性不等式。这是代码的核心。GIN这个函数声明变量x(i,j)为广义整数即非负整数。这是整数规划的关键。3.2 求解与结果分析编写完模型后点击Solve按钮或按CtrlU。LINGO会调用内置的求解器对于线性整数规划通常是分支定界法。求解过程观察状态窗口会显示求解的迭代过程、当前目标函数值、边界值等。对于整数规划你会看到“Best Obj”和“Obj Bound”逐渐接近。求解报告求解完成后会弹出报告窗口。重点关注Global optimal solution found.: 找到了全局最优解这是最理想的情况。Objective value:最优目标函数值即最小化的总运输量或时间成本。Variable部分列出了所有决策变量x(i,j)的最优值。这就是我们需要的车辆安排方案——从每个铲位到每个卸点分别需要运输多少趟。Row部分显示了每个约束条件的松弛/剩余变量Slack or Surplus。如果值为0表示该约束是“紧”的刚好满足如果大于0表示有“松弛”资源未用完。例如某个铲位的供应约束松弛为正值说明该铲位的矿石没有全部运完。结果解读与方案输出 根据x(i,j)的值我们可以整理出清晰的调度表铲位 (i)矿石卸点1 (趟)矿石卸点2 (趟)岩石卸点3 (趟)...总计 (趟)11205...1720810...18..................进一步我们可以计算各路线运输吨数趟数 * 卡车载重。各卸点实际接收品位根据运入的矿石量和品位反算验证是否满足题目要求。卡车使用情况总趟数 * 单趟时间 / 班次时间可以估算出大致需要多少辆卡车在同时运作。这里有一个实用技巧LINGO求出的x是总趟数我们需要将其分配到具体卡车上。一个常用的方法是将总时间除以班次时间再向上取整得到最少卡车数。然后设计一个简单的调度循环让卡车轮流跑这些路线。4. 模型优化与高级技巧基础的模型能解决问题但要拿高分还需要考虑现实复杂性和模型的深化。4.1 多目标处理原题可能隐含多个目标如“产量最大”和“成本最小”。在LINGO中处理多目标常用方法有分层序列法先求解第一个主要目标如满足所有需求将其最优值作为约束再求解第二个目标如最小化卡车数。在LINGO中可以通过分步求解实现。加权求和法将多个目标按重要性赋予权重合并成一个单一目标。例如MIN W1 * 总运量 W2 * 总卡车数。权重的选择需要谨慎可以通过灵敏度分析来观察不同权重对结果的影响。! 加权求和法示例 weight1 0.7; weight2 0.3; MIN weight1 * SUM(link: x * time * capacity) weight2 * SUM(link: x * time / shift_time); ! 第二项近似代表卡车数量4.2 引入卡车分配变量基础模型只规划了总趟数没有具体到每辆车。更精细的模型可以引入二元变量y(k,i,j)卡车k是否执行从i到j的运输任务0或1。这样约束会更复杂例如每辆车时间约束、任务连续性等模型会迅速变大可能超出LINGO免费版的求解能力但思路是完整的。4.3 灵敏度分析与“What-If”问题LINGO的求解报告中的“Dual Price”对偶价格和“Allowable Increase/Decrease”非常有用。它们回答了“如果某个资源如铲位储量、卸点需求稍微改变最优目标值会如何变化”这类问题。这在论文的灵敏度分析部分是非常有力的工具。例如如果某个铲位储量的对偶价格很高说明增加该铲位的储量能显著降低总成本这可以作为矿山运营的建议。5. 常见问题、调试技巧与参赛心得5.1 LINGO建模常见错误排查“No feasible solution found” (找不到可行解)原因约束条件相互矛盾过于严格。排查首先检查数据输入是否正确。然后逐一放松约束。例如先注释掉品位约束和卡车时间约束只保留供需约束看是否有解。再逐步加入其他约束定位导致无解的“元凶”。可能是品位范围定得太窄或者卡车数量/时间根本不足以完成运输任务。“Unbounded solution” (解无界)原因目标函数如求最小成本没有受到有效约束可以无限小。排查检查是否漏掉了关键的资源限制约束如铲位储量约束。确保所有减少目标函数值的变量都有上限约束。求解时间过长或内存不足原因整数规划问题规模太大变量太多或模型非凸。对策简化模型能否将一些整数变量松弛为连续变量例如趟数x如果很大可以先按连续变量求再取整作为初始解。设置求解选项在LINGO的Options-General Solver中可以调整“Branching Direction”分支方向、“Heuristics”启发式等有时能加速。提供初始解如果你根据经验能猜出一个不错的可行解可以在DATA段用INIT部分给变量赋初值能大大缩短求解时间。结果不符合常识现象比如某个路线安排了极多的趟数而另一条更近的路线却为0。排查检查目标函数系数如时间矩阵是否正确。检查约束条件特别是过滤条件是否写错。例如在区分矿石和岩石卸点的约束中条件type(j) #EQ# 1是否准确对应了数据。5.2 数模竞赛中的实战心得论文表述重于求解本身评委知道你是用LINGO解的他们更关心你如何将问题转化为模型以及如何解释结果。在论文中要清晰地写出像上面第2部分那样的数学模型公式而不仅仅是贴LINGO代码。解释清楚每个变量、每个约束的实际意义。结果可视化一张好的图表胜过千言万语。将最优的运输方案用调度甘特图或网络流量图展示出来。可以用Excel或Python的Matplotlib在求解后生成。例如用不同颜色的线条表示不同铲位到卸点的运输流线条粗细代表运量大小。稳定性分析除了LINGO自带的灵敏度分析可以手动做一些“What-If”分析。例如模拟某个铲位因故障产量下降10%重新运行模型观察调度方案如何变化总成本增加多少。这能体现模型的鲁棒性和实用价值。LINGO代码作为附录将完整的、带有注释的LINGO代码放在论文附录里。代码的整洁性和可读性也能体现你的专业素养。备选方案虽然LINGO是主力但在论文中可以提一句“本模型也可用Python的PuLP库或专业求解器Gurobi实现”以展示知识面的广度。但重点一定要放在LINGO的求解过程和结果上。用LINGO 10求解03年这道国赛题就像用一把经典而锋利的解剖刀去剖析一个结构清晰的优化问题。整个过程——从问题理解、模型抽象、代码实现到结果分析——是一个完整的运筹学应用训练。尽管现在工具选择更多了但掌握这种“建模思维”和利用专业工具快速验证想法的能力才是数模竞赛乃至解决实际工程问题的核心。希望这份超详细的复盘能帮你不仅解决这一道题更能触类旁通在面对新的优化问题时知道从哪里入手如何思考。