基于线性规划的“广义指派问题”建模、分解与列生成算法求解示例
我将为您讲解一个尚未出现在列表中的线性规划算法题目。这次我们聚焦于广义指派问题,并介绍如何利用线性规划的Dantzig-Wolfe分解和列生成算法来高效求解大规模实例。与基本的指派问题(如一人一任务)不同,广义指派问题允许一个任务被多个工人(或机器)以不同的效率和成本部分完成,但每个工人的总工作量有限制,且每个任务必须被完全完成。这在实际的资源分配、生产调度中非常常见。
题目描述
假设我们有一组工人(或机器)集合 I = {1, 2, ..., m} 和一组任务集合 J = {1, 2, ..., n}。每个工人 i 拥有一个工作能力(如时间、产能)上限 b_i。将任务 j 分配给工人 i 来处理,会产生一个成本 c_{ij}(例如耗时、费用),并且会消耗工人 i 的 a_{ij} 单位能力。我们的目标是:为每一个任务 j 选择一个或多个工人来共同完成(即允许“拆分”任务),使得总成本最小,同时满足:
- 每个任务 j 必须被完全处理(需求量为1个单位)。
- 每个工人 i 处理所分配任务的总资源消耗不超过其能力上限 b_i。
- 任务 j 分配给工人 i 的比例(或数量)x_{ij} 是非负的。
这是一个经典的、可以建模为线性规划的问题,但直接求解大规模问题(m, n 很大)可能效率不高。列生成算法通过将问题分解,迭代地只生成“有潜力”的决策变量(列)来构建和求解一个简化模型,从而高效处理大规模问题。
解题过程循序渐进讲解
步骤1:建立标准线性规划模型
我们先建立问题的紧凑线性规划模型,称为紧凑公式。
- 决策变量:令 x_{ij} ≥ 0 表示任务 j 分配给工人 i 的比例(或数量)。由于每个任务的总需求为1,这里的 x_{ij} 可以理解为工人 i 承担任务 j 的比例。
- 目标函数:最小化总成本。Minimize: Σ_{i∈I} Σ_{j∈J} c_{ij} * x_{ij}
- 约束条件:
- 任务覆盖约束:每个任务必须被完全分配。对于每个任务 j ∈ J: Σ_{i∈I} x_{ij} = 1
- 工人能力约束:每个工人的资源消耗不能超过上限。对于每个工人 i ∈ I: Σ_{j∈J} a_{ij} * x_{ij} ≤ b_i
- 非负约束:x_{ij} ≥ 0,对所有 i, j。
这个模型是一个线性规划,可以用单纯形法等直接求解。但当 m 和 n 都很大时,变量和约束的数量(m*n + m + n)会变得非常庞大,直接求解计算负担重。
步骤2:Dantzig-Wolfe分解——识别可分解结构
观察上述模型,约束可以被分成两组:
- 耦合约束:任务覆盖约束(Σ_{i∈I} x_{ij} = 1)将所有工人联系在了一起,是“耦合”的。
- 独立约束:工人能力约束(Σ_{j∈J} a_{ij} * x_{ij} ≤ b_i)是按工人分离的。每个工人的约束只涉及分配给该工人的变量 {x_{ij} | j ∈ J}。
这种结构是Dantzig-Wolfe分解的完美应用场景。我们可以将原问题重新表述。
核心思想:对于每个工人 i,考虑其所有可行的工作安排。一个工作安排是指为该工人 i 分配一组任务及其处理量 (x_{i1}, x_{i2}, ..., x_{in}),满足其自身的能力约束 Σ_j a_{ij} x_{ij} ≤ b_i 和 x_{ij} ≥ 0。这样的一个可行安排称为一个“模式”或“列”。
由于每个工人的可行安排集合(称为多面体 P_i)可能包含无穷多个点(因为 x_{ij} 是连续的),我们无法枚举所有模式。Dantzig-Wolfe分解告诉我们,原问题的最优解可以表示为各个工人可行多面体 P_i 中极点的凸组合。
步骤3:构建主问题(Master Problem, MP)和定价子问题(Pricing Subproblem, PSP)
基于分解思想,我们构建一个等价的新模型:
- 主问题(MP)决策变量:假设我们(暂时)知道工人 i 的所有可行模式的集合 S_i。设 λ_{ik} 是一个连续变量,表示在最终解中,工人 i 采用其第 k 个模式的程度(比例)。注意,对于一个工人,我们可以组合使用多个模式。
- 主问题(MP)模型:
- 设对于工人 i 的可行模式 k,其参数为:
- c_{ik} = 该模式的成本 = Σ_j c_{ij} * x_{ij}^k
- d_{jk}^i = 该模式处理任务 j 的量 = x_{ij}^k
- 目标:Minimize Σ_{i∈I} Σ_{k∈S_i} c_{ik} * λ_{ik}
- 约束:
- 任务覆盖约束:对于每个任务 j,所有工人所有模式对其的处理量总和必须为1。即:Σ_{i∈I} Σ_{k∈S_i} d_{jk}^i * λ_{ik} = 1, ∀ j ∈ J。
- 凸组合约束:对于每个工人 i,其使用的所有模式的程度之和为1(这意味着工人 i 的工作安排是它所有可行模式的一个凸组合)。即:Σ_{k∈S_i} λ_{ik} = 1, ∀ i ∈ I。
- 非负约束:λ_{ik} ≥ 0。
- 设对于工人 i 的可行模式 k,其参数为:
- 这个MP模型与原问题等价,但变量是 λ_{ik}。如果我们能枚举所有 S_i,MP 可以直接求解。但 S_i 是无穷集,我们无法全部列出。
列生成算法的智慧在于:我们不需要一开始就知道所有 S_i。我们从每个工人 i 的一个很小的、初始的可行模式集合 S_i'(可能只是一个简单模式,比如不处理任何任务,即零模式)开始,求解一个限制主问题。
步骤4:初始限制主问题与对偶变量
- 初始化:为每个工人 i 构建一个或多个简单的可行模式,加入 S_i'。最常用的初始模式是“空模式”(所有 x_{ij}=0),其成本为0,不处理任何任务。这保证了限制主问题是可行的(虽然任务覆盖约束可能无法满足,可能需要引入人工变量或使用其他启发式方法生成更好的初始列,这里为简化,我们假设初始模式集合能使限制主问题可行)。
- 求解限制主问题:用线性规划求解器(如单纯形法)求解当前的限制主问题,得到当前最优解 λ* 和最重要的——对偶变量的值。
- 设任务覆盖约束(Σ_i Σ_k d_{jk}^i * λ_{ik} = 1)对应的对偶变量为 π_j (每个任务 j 一个)。
- 设凸组合约束(Σ_k λ_{ik} = 1)对应的对偶变量为 ω_i (每个工人 i 一个)。
步骤5:定价子问题——寻找“有负检验数”的新列
在单纯形法中,引入一个新变量(列)是否有利,取决于它的检验数(Reduced Cost)是否为负(对于最小化问题)。在我们的MP模型中,对于工人 i 的一个新模式(其决策变量为 λ_{i, new}),其检验数计算如下:
检验数 = c_{i, new} - Σ_j π_j * d_{j, new}^i - ω_i
其中,c_{i, new} = Σ_j c_{ij} * x_{ij},d_{j, new}^i = x_{ij}。所以,
检验数 = Σ_j c_{ij} * x_{ij} - Σ_j π_j * x_{ij} - ω_i = Σ_j (c_{ij} - π_j) * x_{ij} - ω_i
我们的目标是:对于每个工人 i,尝试找到一个可行的新模式(即一组满足工人 i 自身能力约束的 x_{ij} ≥ 0),使得其检验数为负。 如果找到了,将这个新模式作为一列加入限制主问题,因为它有可能改进当前解。
这引导出 m 个独立的定价子问题,每个对应一个工人 i:
定价子问题 i(对工人 i):
Minimize Σ_{j∈J} (c_{ij} - π_j) * x_{ij} - ω_i
Subject to:
Σ_{j∈J} a_{ij} * x_{ij} ≤ b_i
x_{ij} ≥ 0, ∀ j ∈ J
注意,ω_i 是一个常数。所以,子问题 i 等价于求解一个简单的线性规划(实际上是一个连续背包问题):
Minimize Σ_{j∈J} (c_{ij} - π_j) * x_{ij}
Subject to:
Σ_{j∈J} a_{ij} * x_{ij} ≤ b_i
x_{ij} ≥ 0, ∀ j ∈ J
步骤6:列生成迭代过程
- 求解当前限制主问题,得到对偶变量 π 和 ω。
- 遍历所有工人 i,求解对应的定价子问题 i。
- 检查终止条件:如果对于所有工人 i,其定价子问题的最优值(即最小化的 Σ_j (c_{ij} - π_j) * x_{ij})都大于等于 ω_i,那么意味着所有可能新列的检验数都 ≥ 0。根据线性规划的对偶理论,当前限制主问题的解就是原主问题(MP)的最优解,算法终止。
- 添加新列:如果存在某个工人 i’,其定价子问题的最优值 < ω_i’,那么我们就找到了一个检验数为负的新模式(由子问题的最优解 x_{ij}^* 定义)。将这个新模式添加到限制主问题中,对应工人 i’ 的新变量 λ_{i’, new}。其在该列中的系数为:
- 在目标函数中的系数:c_{i’, new} = Σ_j c_{i’j} * x_{i’j}^*
- 在任务覆盖约束 j 中的系数:d_{j, new}^{i’} = x_{i’j}^*
- 在凸组合约束 i’ 中的系数:1
- 回到第1步,用新的列集合求解更新后的限制主问题。
步骤7:获取原问题解
当列生成算法终止时,我们得到了限制主问题(现在等价于完整MP)的最优解 λ*。我们需要将其“转换”回原问题的 x_{ij} 变量。
原问题的解为:对于每个工人 i 和任务 j,x*{ij} = Σ{k∈S_i} (x_{ij}^k * λ_{ik}^),其中 x_{ij}^k 是模式 k 中工人 i 处理任务 j 的量,λ_{ik}^ 是主问题中该模式的使用程度。
总结与核心思路
这个算法的美妙之处在于,它将一个大规模线性规划(变量数为 m*n)分解为:
- 一个“主协调”问题:负责确保所有任务被覆盖,并协调各工人的模式选择。其规模只与任务数 n 和当前生成的列数有关,通常远小于 m*n。
- m 个“局部”子问题:每个子问题只关心一个工人,结构简单(一个资源约束的线性规划),可以并行高效求解。
通过迭代地在“主问题”和“子问题”之间交换对偶信息(π_j),算法能够“按需”生成有价值的决策模式,避免了对所有 m*n 种可能的分配组合进行显式枚举和存储,从而能够高效求解大规模的广义指派问题。这正是列生成和Dantzig-Wolfe分解在处理具有可分离结构的大规模线性规划时的威力所在。