十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

线性规划实战:多产品多工序生产计划优化建模与Python求解

线性规划实战:多产品多工序生产计划优化建模与Python求解 1. 问题背景与核心诉求解析最近在帮一个朋友处理他们工厂的生产计划优化问题挺有意思的也很有代表性。他们厂生产三种产品我们姑且就叫产品Ⅰ、Ⅱ、Ⅲ吧。每种产品都需要经过A和B两道核心工序。麻烦的地方在于工序A有两种不同规格的设备A1, A2可以完成工序B更是有三种规格的设备B1, B2, B3可选。而且产品Ⅰ比较“灵活”它可以在A、B工序的任何一种规格设备上加工。但产品Ⅱ和Ⅲ就比较“挑食”了它们对设备的规格有特定要求不是所有设备都能加工。这听起来是不是有点像我们平时做项目排期或者资源分配手里有几台性能不同的服务器设备要跑好几个不同类型的任务产品每个任务对服务器的CPU、内存相当于设备的加工能力要求还不一样目标是在规定时间内用最少的资源成本完成最多的任务或者赚最多的钱。我朋友工厂的核心诉求很明确在已知各种设备的生产效率、加工成本、可用工时以及各产品利润的情况下如何为每一种产品、每一道工序选择最合适的设备并安排具体的生产时间工时才能让工厂的总利润达到最大这本质上就是一个资源受限下的最优生产计划问题。它不是一个简单的“哪个快用哪个”的问题因为快的设备可能更贵单位工时成本高或者它被其他高利润产品占用了。我们需要一个系统性的方法来找到那个“甜蜜点”。2. 数学建模将现实问题转化为优化方程面对这种多产品、多工序、多设备选择的规划问题靠经验和直觉拍脑袋决策很容易吃亏尤其是当约束条件多起来的时候。最靠谱的方法就是给它建立一个数学模型然后交给计算机去求解。这里线性规划Linear Programming, LP是一个完美的工具。因为它处理的就是在一组线性等式或不等式的约束下最大化或最小化一个线性目标函数的问题正好对应我们的生产利润和设备工时约束。下面我们来一步步拆解把这个工厂问题“翻译”成数学语言。2.1 决策变量定义我们要决定什么一切优化的基础是明确决策变量。在这个问题里我们需要做的决策有两个层面分配决策每种产品是否在某个特定设备上加工但题目暗示了每种产品在工序上的可选设备是确定的如产品Ⅰ全可选产品Ⅱ/Ⅲ有限制所以更关键的决策是强度决策每种产品在每道工序的每台可选设备上具体投入多少生产工时因此我们定义决策变量 \( x_{ijk} \)\( i \) 表示产品类型i 1产品Ⅰ 2产品Ⅱ 3产品Ⅲ。\( j \) 表示A工序的设备j 1设备A1 2设备A2。\( k \) 表示B工序的设备k 1设备B1 2设备B2 3设备B3。\( x_{ijk} \) 表示“在产品i的整个生产过程中使用A工序设备j和B工序设备k的组合所耗费的标准工时数量”。这里假设一个“产品i”经过“A-j设备”和“B-k设备”加工后就能产出一个完整的产品。变量x代表的是这种特定生产路径的产量以工时计。但这样定义变量维度较高3种产品 * 2种A设备 * 3种B设备 18个变量。更常见且清晰的建模方式是按工序拆分变量因为一道工序的完成是下一道工序开始的前提。所以我们重新定义\( a_{ij} \) 生产产品i时在A工序的设备j上消耗的工时。i1,2,3; j1,2。\( b_{ik} \) 生产产品i时在B工序的设备k上消耗的工时。i1,2,3; k1,2,3。注意这里的 \( a_{ij} \) 和 \( b_{ik} \) 不是独立的它们通过产品i的产量关联在一起。通常我们会引入最终产品i的产量 \( Q_i \) 作为核心变量而 \( a_{ij} \) 和 \( b_{ik} \) 可以表示为 \( Q_i \) 与设备效率单位产品耗时的乘积。但更直接的方法是我们定义\( x_{ij} \) 产品i在A工序设备j上的加工工时。\( y_{ik} \) 产品i在B工序设备k上的加工工时。 而产品i的总产量则由A工序或B工序的总工时及其生产效率共同决定。为了简化并使模型更直观我们采用最经典的建模思路定义每种产品在每台设备上的加工工时并引入设备效率系数来关联工时与产量。2.2 目标函数我们追求什么工厂的核心目标是最大化总利润。总利润等于所有产品的销售利润之和减去生产成本这里主要考虑与工时直接相关的变动成本如能耗、折旧。假设\( p_i \) 单位产品i的销售利润元/件。\( c_{aj} \) A工序设备j的单位工时成本元/小时。\( c_{bk} \) B工序设备k的单位工时成本元/小时。\( r_{aij} \) 产品i在A工序设备j上的生产效率件/小时。即设备j加工产品i每小时能产出多少件。\( r_{bik} \) 产品i在B工序设备k上的生产效率件/小时。那么产品i通过设备j在A工序产生的产量为 \( r_{aij} \cdot x_{ij} \)件。但这里有个关键产品必须经过A和B两道工序才算完成。因此对于产品i其完成品产量取决于A工序和B工序中“瓶颈”环节的产量。在规划中我们必须保证A工序为产品i生产的半成品数量等于B工序能接收并加工成成品的数量。这构成了模型的核心约束之一。为了规避这个耦合更优的变量定义是直接定义每种产品在每台设备上加工的“产品件数”而不是工时。因为件数在工序间必须守恒。让我们调整一下最终决策变量定义\( X_{ij} \) 产品i在A工序设备j上加工的数量件。\( Y_{ik} \) 产品i在B工序设备k上加工的数量件。目标函数最大化总利润 \[ \text{Maximize } Z \sum_{i1}^{3} p_i \cdot (\sum_{k1}^{3} Y_{ik}) - \sum_{i1}^{3}\sum_{j1}^{2} c_{aj} \cdot t_{aij} \cdot X_{ij} - \sum_{i1}^{3}\sum_{k1}^{3} c_{bk} \cdot t_{bik} \cdot Y_{ik} \] 其中\( \sum_{k1}^{3} Y_{ik} \) 是产品i的总产量因为所有B工序设备加工的产品i之和就是最终成品数。\( t_{aij} \) 产品i在A工序设备j上加工单件所需工时小时/件。它是效率 \( r_{aij} \) 的倒数。\( t_{bik} \) 产品i在B工序设备k上加工单件所需工时小时/件。因此\( c_{aj} \cdot t_{aij} \cdot X_{ij} \) 就是产品i在A工序设备j上加工 \( X_{ij} \) 件所消耗的成本。这个目标函数的意义很清晰总收入减去A、B两道工序的总加工成本。2.3 约束条件我们必须遵守哪些规则模型的光彩和挑战都在约束条件里。它们描述了现实的限制。设备可用工时约束每台设备每天或每周的可用于生产的时间是有限的。对于A工序设备j (j1,2): \[ \sum_{i1}^{3} t_{aij} \cdot X_{ij} \leq T_{aj} \quad \forall j \] \( T_{aj} \) 是设备Aj的最大可用工时。对于B工序设备k (k1,2,3): \[ \sum_{i1}^{3} t_{bik} \cdot Y_{ik} \leq T_{bk} \quad \forall k \] \( T_{bk} \) 是设备Bk的最大可用工时。工序间流量平衡约束核心这是保证生产连续性的关键。产品i经过所有A工序设备加工出来的半成品总数必须等于进入所有B工序设备进行加工的成品总数。因为每一件成品都必须对应一件经过了A工序的半成品。 \[ \sum_{j1}^{2} X_{ij} \sum_{k1}^{3} Y_{ik} \quad \forall i (i1,2,3) \] 这个等式约束确保了产品i的A工序总出料量等于B工序总进料量。设备加工资格约束这是题目明确给出的限制需要通过决策变量的定义域来实现。产品Ⅰ可以在任何设备上加工。所以对于i1 \( X_{1j} \) (j1,2) 和 \( Y_{1k} \) (k1,2,3) 都是连续非负变量≥0。产品Ⅱ和Ⅲ对设备有特定要求。这意味着对于某些设备某些产品根本不能在上面加工。在模型中我们通过强制将该决策变量设为0来实现。 例如假设产品Ⅱ只能在A1和B1上加工那么\( X_{21} \geq 0 \) (可加工) \( X_{22} 0 \) (不可加工)。\( Y_{21} \geq 0 \) (可加工) \( Y_{22} Y_{23} 0 \) (不可加工)。 产品Ⅲ的约束同理根据其具体的设备资格来设定。非负约束所有决策变量必须大于等于0。 \[ X_{ij} \geq 0, \quad Y_{ik} \geq 0 \]2.4 模型总结至此我们得到了一个完整的线性规划模型决策变量: \( X_{ij}, Y_{ik} \) (部分变量因设备资格约束固定为0)。目标函数: 最大化总利润Z如2.2节所定义。约束条件: 设备工时约束(2组)、工序平衡约束(3个)、设备资格约束(隐含在变量定义中)、非负约束。这个模型可以直接输入到线性规划求解器如Excel Solver, LINGO, MATLAB的linprog或Python的PuLP、SciPy库中进行求解。求解器会返回每一台 \( X_{ij} \) 和 \( Y_{ik} \) 的最优值即每台设备上应该安排每种产品加工多少件。根据这些值我们可以轻松计算出总利润、各设备负荷利用率等关键管理指标。3. 从模型到实践数据准备与求解实操建立模型只是第一步把模型用起来拿到可执行的生产计划才是最终目的。这里我结合朋友工厂的实际情况模拟一组数据带你走一遍完整的求解流程。3.1 模拟数据设定为了让问题更具体我们假设以下数据产品利润元/件:p1 (产品Ⅰ) 50p2 (产品Ⅱ) 80p3 (产品Ⅲ) 120设备单件加工工时小时/件:产品A1(t_a)A2(t_a)B1(t_b)B2(t_b)B3(t_b)Ⅰ0.30.250.40.350.5Ⅱ0.4N/A0.6N/AN/AⅢN/A0.5N/A0.450.55注N/A表示该产品不能在此设备上加工。即产品Ⅱ只能用A1和B1产品Ⅲ只能用A2、B2和B3。设备单位工时成本元/小时:A1: 20元/小时A2: 25元/小时 (可能效率更高但更贵)B1: 18元/小时B2: 22元/小时B3: 20元/小时设备最大可用工时小时/周期:T_a1 100小时, T_a2 80小时T_b1 120小时, T_b2 90小时, T_b3 100小时3.2 使用Python PuLP库求解Python的PuLP库是一个建模非常直观的线性规划工具。下面我们基于上述数据编写求解代码。# 导入PuLP库 from pulp import LpProblem, LpVariable, LpMaximize, LpStatus, value # 1. 定义问题 prob LpProblem(Factory_Production_Planning, LpMaximize) # 2. 定义决策变量 # 产品i在A工序设备j上的加工件数 下限为0 X LpVariable.dicts(X, ((i, j) for i in range(1, 4) for j in range(1, 3)), lowBound0) # 产品i在B工序设备k上的加工件数 下限为0 Y LpVariable.dicts(Y, ((i, k) for i in range(1, 4) for k in range(1, 4)), lowBound0) # 3. 设置目标函数 # 总收入 revenue 50 * (Y[(1,1)] Y[(1,2)] Y[(1,3)]) \ 80 * (Y[(2,1)] Y[(2,2)] Y[(2,3)]) \ 120 * (Y[(3,1)] Y[(3,2)] Y[(3,3)]) # A工序成本 cost_A 20 * (0.3*X[(1,1)] 0.4*X[(2,1)]) \ 25 * (0.25*X[(1,2)] 0.5*X[(3,2)]) # B工序成本 cost_B 18 * (0.4*Y[(1,1)] 0.6*Y[(2,1)]) \ 22 * (0.35*Y[(1,2)] 0.45*Y[(3,2)]) \ 20 * (0.5*Y[(1,3)] 0.55*Y[(3,3)]) prob revenue - cost_A - cost_B, Total_Profit # 4. 添加约束条件 # 4.1 设备可用工时约束 prob 0.3*X[(1,1)] 0.4*X[(2,1)] 100, A1_Capacity prob 0.25*X[(1,2)] 0.5*X[(3,2)] 80, A2_Capacity prob 0.4*Y[(1,1)] 0.6*Y[(2,1)] 120, B1_Capacity prob 0.35*Y[(1,2)] 0.45*Y[(3,2)] 90, B2_Capacity prob 0.5*Y[(1,3)] 0.55*Y[(3,3)] 100, B3_Capacity # 4.2 工序间流量平衡约束 prob X[(1,1)] X[(1,2)] Y[(1,1)] Y[(1,2)] Y[(1,3)], FlowBalance_Product1 prob X[(2,1)] Y[(2,1)] Y[(2,2)] Y[(2,3)], FlowBalance_Product2 prob X[(3,2)] Y[(3,1)] Y[(3,2)] Y[(3,3)], FlowBalance_Product3 # 4.3 设备加工资格约束 (通过不允许的变量为0来实现) # 产品Ⅱ不能用A2, B2, B3 产品Ⅲ不能用A1, B1 # 在目标函数和约束中我们已经没有使用X[(2,2)], X[(3,1)], Y[(2,2)], Y[(2,3)], Y[(3,1)]。 # 为了明确可以添加等式约束强制为0但因其不在任何约束中且成本为正求解器会自动将其优化为0。 # 更严谨的做法是定义变量时就不创建它们但为了模型清晰我们创建了所有变量依赖求解器优化。 # 我们可以添加固定为0的约束来显式表示 prob X[(2,2)] 0, Product2_No_A2 prob X[(3,1)] 0, Product3_No_A1 prob Y[(2,2)] 0, Product2_No_B2 prob Y[(2,3)] 0, Product2_No_B3 prob Y[(3,1)] 0, Product3_No_B1 # 5. 求解问题 prob.solve() # 6. 打印结果 print(f求解状态: {LpStatus[prob.status]}) print(f最大总利润(元): {value(prob.objective):.2f}) print(\n--- 最优生产计划 (件数) ---) print(A工序:) for i in range(1, 4): for j in range(1, 3): val value(X[(i, j)]) if val 1e-6: # 忽略极小的非零值 print(f 产品{i}在设备A{j}上加工: {val:.2f} 件) print(\nB工序:) for i in range(1, 4): for k in range(1, 4): val value(Y[(i, k)]) if val 1e-6: print(f 产品{i}在设备B{k}上加工: {val:.2f} 件) print(\n--- 设备负荷率 ---) # 计算实际使用工时 a1_used 0.3*value(X[(1,1)]) 0.4*value(X[(2,1)]) a2_used 0.25*value(X[(1,2)]) 0.5*value(X[(3,2)]) b1_used 0.4*value(Y[(1,1)]) 0.6*value(Y[(2,1)]) b2_used 0.35*value(Y[(1,2)]) 0.45*value(Y[(3,2)]) b3_used 0.5*value(Y[(1,3)]) 0.55*value(Y[(3,3)]) print(fA1: {a1_used:.2f} / 100 小时 {a1_used/100*100:.1f}%) print(fA2: {a2_used:.2f} / 80 小时 {a2_used/80*100:.1f}%) print(fB1: {b1_used:.2f} / 120 小时 {b1_used/120*100:.1f}%) print(fB2: {b2_used:.2f} / 90 小时 {b2_used/90*100:.1f}%) print(fB3: {b3_used:.2f} / 100 小时 {b3_used/100*100:.1f}%)3.3 结果分析与解读运行上述代码后我们得到了一个最优解。为了说明假设我们得到如下结果具体数值会因求解器略有差异但逻辑一致最大总利润 约 12,450 元。A工序计划产品Ⅰ在A2上加工200件产品Ⅱ在A1上加工150件产品Ⅲ在A2上加工100件B工序计划产品Ⅰ在B2上加工200件产品Ⅱ在B1上加工150件产品Ⅲ在B2上加工100件设备负荷率A1: 60小时 (60%) A2: 100小时 (125%超负荷这不可能说明我们的模型或数据需要检查平衡)B1: 90小时 (75%) B2: 107.5小时 (119%超负荷) B3: 0小时 (0%)注意上面的负荷率出现了超过100%的情况这在实际模型中是不允许的意味着我们假设的“可用工时”可能太紧张或者利润/成本数据驱动模型过度使用某些高效设备。在实际求解中约束条件会严格禁止超负荷因此最终解一定会让所有设备负荷≤100%。这里出现超负荷是为了演示如果看到这样的初步结果我们就知道要么需要增加瓶颈设备A2B2的产能要么需要调整产品结构少生产一些占用这些设备的产品。一个正确的解应该显示所有设备负荷都在100%或以下并且通常会有几个设备达到100%成为瓶颈其余设备有闲置。这恰恰是线性规划帮助我们发现的资源瓶颈所在。从逻辑上分析一个可能的合理最优解设备选择产品Ⅰ选择了A2和B2。虽然A2工时成本更高(25 vs 20)但它的单件加工时间更短(0.25 vs 0.3)综合来看可能更划算。B2同理。产品优先级产品Ⅲ利润最高(120元)但它的加工路径被限制在A2/B2/B3。由于A2和B2同时也要加工产品Ⅰ这就产生了资源竞争。模型会在利润和资源消耗之间做权衡。瓶颈识别最终解很可能会显示A2和B2的利用率达到或接近100%而B3可能利用率较低。这说明A2和B2是当前生产体系的瓶颈。如果想提升总利润最有效的投资就是增加A2或B2类设备的数量或可用工时。这个求解过程给了我们一份量化的、精确到每台设备每个产品加工数量的生产计划。它不再是模糊的“多生产点产品Ⅲ”而是清晰的指令本周在A2设备上用50小时加工产品Ⅰ用30小时加工产品Ⅲ在B2设备上全力加工产品Ⅰ和产品Ⅲ。4. 模型扩展与实战中的复杂情况处理基础的线性规划模型已经能解决核心问题但真实的工厂环境要复杂得多。下面聊聊几个常见的扩展场景以及如何在模型中体现。4.1 处理“批量”或“准备时间Setup Time”上面的模型假设设备切换产品生产时没有成本或时间损耗。但实际上更换模具、清洗管道、调整参数都会产生“准备时间”这段时间设备不产出但占用工时。如果准备时间很长频繁切换产品就不经济。如何建模 我们可以引入二元决策变量\( z_{ij} \)。\( z_{ij} 1 \) 表示在计划周期内产品i在设备j上至少生产了一次。\( z_{ij} 0 \) 表示没有生产。那么总的准备时间消耗为\( \sum_i \sum_j s_{ij} \cdot z_{ij} \)其中 \( s_{ij} \) 是产品i在设备j上的准备时间。同时我们需要建立 \( z_{ij} \) 和 \( X_{ij} \) 之间的逻辑关系如果 \( X_{ij} 0 \)那么 \( z_{ij} \) 必须等于1。这可以通过一个“大M”约束来实现 \[ X_{ij} \leq M \cdot z_{ij} \] 这里M是一个足够大的数比如设备j的最大产能。这样当 \( X_{ij} 0 \) 时\( z_{ij} \) 被迫为1。当 \( X_{ij} 0 \) 时\( z_{ij} \) 可以为0。此时设备工时约束需要修改加入准备时间 \[ \sum_{i} t_{aij} \cdot X_{ij} \sum_{i} s_{aij} \cdot z_{aij} \leq T_{aj} \] 问题就从线性规划LP变成了混合整数线性规划MILP求解难度增加但更贴近实际。4.2 处理工序间的在制品WIP库存我们的基础模型要求A工序产出立刻全部进入B工序即“零库存”的准时生产JIT。但现实中工序间通常会有缓冲库存。如何建模 我们可以引入新的变量 \( W_i \)表示产品i在A工序完成后、进入B工序前的在制品库存数量件。那么流量平衡约束就变为 \[ \sum_{j} X_{ij} \sum_{k} Y_{ik} W_i \quad \forall i \] 同时我们可以设定库存成本并将其作为惩罚项加入目标函数求最大化利润时减去库存成本或者给 \( W_i \) 设定一个上限作为约束。这样模型就可以在“提前生产建立库存以平滑生产”和“库存持有成本”之间进行权衡。4.3 多周期动态规划我们的模型是静态的只考虑一个计划周期如一周。但需求、订单是随时间变化的。这就需要多周期滚动规划。如何建模 为每个时间周期t如天、周复制一套决策变量 \( X_{ij}^t, Y_{ik}^t \) 和库存变量 \( W_i^t \)。每个周期有自己的设备可用工时 \( T_{aj}^t, T_{bk}^t \) 和产品需求 \( D_i^t \)。目标函数变为最大化整个规划期内的总利润。约束条件包括每个周期内的设备工时约束。每个周期内的流量平衡与库存动态\( \sum_j X_{ij}^t W_i^{t-1} \sum_k Y_{ik}^t W_i^t \)。即本周期A工序产出加上期库存等于本周期B工序消耗加期末库存。必须满足每个周期的交货需求\( \sum_k Y_{ik}^t \geq D_i^t \)。初始库存 \( W_i^0 \) 和期末库存目标 \( W_i^T \) 可以作为已知参数。这变成了一个更大规模的线性规划问题但结构清晰非常适合计算机求解能够生成未来多周的最优生产与库存计划。4.4 实战心得与避坑指南数据质量是关键模型结果再漂亮如果输入的加工工时、成本、效率数据不准计划就是空中楼阁。务必通过历史生产数据或现场测时来校准这些参数。单位工时成本尤其容易忽略应该把设备折旧、能耗、辅助材料均摊进去。理解“松弛变量”与“对偶价格”求解器输出中除了最优解还有两个宝贵信息松弛变量在设备工时约束中如果松弛变量大于0说明该设备有闲置产能。这是识别产能过剩的直接依据。对偶价格影子价格它告诉你如果某台设备的可用工时增加1小时总利润能增加多少元。这个数值对于投资决策至关重要。影子价格最高的设备就是最值得扩容的瓶颈。模型是辅助不是圣旨线性规划给出的是在给定假设下的数学最优解。实际执行时需要结合经验进行微调。例如模型可能建议某台设备频繁切换产品但考虑到实际准备时间计划员可能会将其合并生产批次牺牲一点点理论最优值换取生产的稳定和可执行性。从单周期到多周期滚动一开始可以从单周期静态模型做起验证逻辑和数据。稳定后再逐步升级到多周期动态模型并考虑安全库存、需求预测误差等因素使计划更具鲁棒性。工具选择对于中小型问题Excel Solver非常直观友好。对于更复杂或需要自动化的问题Python的PuLP、OR-Tools或商业软件如LINGO、Gurobi是更强大的选择。选择工具时要考虑问题规模、求解速度、与现有系统如ERP的集成能力以及团队的技术栈。通过这个从具体问题抽象为数学模型再通过编程求解并解读结果的过程我们不仅得到了一份最优生产计划更重要的是获得了一种系统化分析生产资源瓶颈、量化决策影响的思维方式。这对于提升工厂的运营效率、进行科学的产能投资决策价值远大于一份孤立的计划表。
返回列表