基于分支定界法的带卡车容量约束的运输问题求解教程

2026-06-03 91127 日常编程

本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法(由求解器自动执行)求得全局最优解,重点在于建模技巧与变量耦合设计。

本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法(由求解器自动执行)求得全局最优解,重点在于建模技巧与变量耦合设计。

在标准运输问题中,决策变量通常表示从供应点 $i$ 到需求点 $j$ 的连续运量 $x{ij}$,目标是最小化总运费 $\sum{i,j} c{ij} x{ij}$。但当实际物流受限于固定载重卡车(如本例中每辆卡车仅能运6吨)时,运费不再与运量呈严格线性关系——例如运10吨需2辆卡车,产生费用 $2 \times 6 \times c{ij} = 12c{ij}$,而非 $10c_{ij}$。此时,问题本质变为整数资源调度问题,必须引入整数变量显式刻画卡车使用数量。

核心建模思想是:解耦“运量”与“成本”,通过整数卡车变量 $t{ij} \in \mathbb{Z}+$ 控制成本,并用线性约束强制其与运量 $x_{ij}$ 逻辑一致。具体实现如下:

  • 定义连续变量 $x_{ij} \geq 0$ 表示实际运输吨数;
  • 定义整数变量 $t_{ij} \in {0,1,2,\dots}$ 表示从 $i$ 到 $j$ 调用的6吨卡车数量;
  • 引入容量耦合约束:$x{ij} \leq 6 \cdot t{ij}$,确保卡车数量足以承载运量(注意:该约束不强制“满载”,但结合最小化目标,最优解天然趋向满载以节省卡车);
  • 目标函数改为:$\min \sum{i,j} (6 \cdot c{ij}) \cdot t_{ij}$,即每辆卡车固定承担6吨×单位运费的成本。

此模型构成典型的混合整数线性规划(MILP),其求解依赖分支定界(Branch-and-Bound)框架:求解器首先松弛 $t{ij}$ 为连续变量获得下界;随后对非整数 $t{ij}$ 进行分支(如 $t{12} \leq 1$ 或 $t{12} \geq 2$),递归求解子问题并剪枝,最终收敛至整数最优解。

以下为完整可运行代码(基于 PuLP 库,语法清晰且开源免费):

import pandas as pd
import pulp

# 参数定义
truck_capacity = 6
suppliers = pd.RangeIndex(name='supplier', stop=4)   # 4个供应点
consumers = pd.RangeIndex(name='consumer', stop=5)   # 5个需求点

supply = pd.Series([17, 8, 10, 9], index=suppliers, name='supply')
demand = pd.Series([6, 15, 7, 8, 8], index=consumers, name='demand')
cost_matrix = pd.DataFrame([
    [10,  8,  5,  9, 16],
    [ 4,  3,  4, 11, 12],
    [ 5, 10, 29,  7,  6],
    [ 9,  2,  4,  1,  3]
], index=suppliers, columns=consumers)

# 决策变量:运量(连续)与卡车数(整数)
flow = pd.DataFrame(
    pulp.LpVariable.matrix('flow_s%d_c%d', (suppliers, consumers), 
                          cat='Continuous', lowBound=0)
).stack().rename('flow')

trucks = pd.DataFrame(
    pulp.LpVariable.matrix('trucks_s%d_c%d', (suppliers, consumers), 
                          cat='Integer', lowBound=0)
).stack().rename('trucks')

# 目标函数:最小化总卡车成本(每辆卡车运6吨 × 单位运费)
price_per_truck = cost_matrix.stack() * truck_capacity
prob = pulp.LpProblem("Transportation_with_Trucks", pulp.LpMinimize)
prob.setObjective(pulp.lpDot(price_per_truck, trucks))

# 约束:供应上限
for i in suppliers:
    prob.addConstraint(pulp.lpSum(flow.xs(i, level='supplier')) <= supply[i], 
                      name=f'supply_{i}')

# 约束:需求必须精确满足(等式约束更符合实际)
for j in consumers:
    prob.addConstraint(pulp.lpSum(flow.xs(j, level='consumer')) == demand[j], 
                      name=f'demand_{j}')

# 约束:卡车容量限制(关键!)
for (i, j), f in flow.items():
    prob.addConstraint(f <= trucks[(i, j)] * truck_capacity, 
                      name=f'capacity_{i}_{j}')

# 求解
prob.solve(pulp.PULP_CBC_CMD(msg=1))  # 使用CBC开源求解器
assert prob.status == pulp.LpStatusOptimal, "求解失败"

# 输出结果
print("✅ 最优总成本:", pulp.value(prob.objective))
print("\n? 运输量矩阵(吨):")
print(flow.apply(pulp.value).unstack(level='consumer').round(1))
print("\n? 卡车调用矩阵(辆):")
print(trucks.apply(pulp.value).unstack(level='consumer'))

关键注意事项:

  • 必须使用等式约束满足需求(== demand[j]),避免松弛导致欠运;供应端可用 <= 保留灵活性。
  • ⚠️ 避免强整数约束冗余:无需添加 $t{ij} \ge \lceil x{ij}/6 \rceil$ 类非线性约束,x_{ij} \le 6 t_{ij} 已足够,且保持模型线性。
  • ? 验证解的合理性:检查每条路径是否满足 $x{ij} > 0 \Rightarrow t{ij} \ge 1$,且 $t{ij} = 0 \Rightarrow x{ij} = 0$(由约束和最优性保证)。
  • ? 扩展性提示:若卡车有不同规格,可引入多类型整数变量及对应约束;若存在固定调度成本,需添加 $y{ij} \in {0,1}$ 指示是否启用该路线,并添加 $x{ij} \le M y{ij}$ 和 $t{ij} \le M y_{ij}$。

该方法将现实中的离散资源约束自然融入运筹模型,无需手动实现分支定界算法——现代求解器(如 Gurobi、CPLEX、CBC)均内置高效B&B引擎,用户只需专注正确建模,即可获得理论最优解。

基于分支定界法求解带卡车容量约束的运输问题(MILP建模教程)

本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法自动求解最优运输方案与卡车调度计划。 本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法自动求解最优运输方案与卡车调度计划。 在标准运输问题中,决策变量是连续的运量(如“从供应点1向需求点2运送10吨货物”),目标是最小化总运费(单位...

使用分支定界法求解带卡车容量约束的运输问题(MILP建模实践)

本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法自动求解最优运输方案——核心是引入整数“卡车数量”变量,通过容量耦合约束将连续运量与离散车辆调度统一建模。 本文介绍如何将经典运输问题扩展为考虑6吨卡车整数装载限制的混合整数线性规划(milp)问题,并利用分支定界法自动求解最优运输方案——核心是引入整数“卡车数量”变量,通过容量耦合约束将连...

如何处理Python Django中的IntegrityError外键约束冲突_通过清理脏数据或设置on_delete属性

IntegrityError“违反外键约束”是数据库拒绝非法外键引用,因插入/更新记录的外键值在被引用表中不存在;需清理孤立数据、加存在性校验、合理配置on_delete。 为什么IntegrityError会报“insertorupdateontableXviolatesforeignkeyconstraint” 这错误不是代码写错了,而是数据库在拦你:你正试图插入或更新一条记录,它的外键字段(...

高效求解含零块的分块对角矩阵伪逆(保持零块不变)

本文介绍一种针对大规模分块对角矩阵的高效逆运算方法:仅对非零3×3对角块求逆,保留零块为零,避免全矩阵求逆开销,时间复杂度降至o(n),适用于n≈10⁴量级的稀疏块结构。 本文介绍一种针对大规模分块对角矩阵的高效逆运算方法:仅对非零3×3对角块求逆,保留零块为零,避免全矩阵求逆开销,时间复杂度降至o(n),适用于n≈10⁴量级的稀疏块结构。 在科学计算与算子建模中,常遇到结构化的大规模矩阵——尤其...

如何高效求解含零块的分块对角矩阵的伪逆(保持零块不变)

本文介绍一种针对大规模分块对角矩阵的高效逆运算方法:仅对非零3×3对角块求逆,跳过零块并保持其为零,避免全矩阵运算,时间复杂度降至o(n),适用于n≈10⁴量级场景。 本文介绍一种针对大规模分块对角矩阵的高效逆运算方法:仅对非零3×3对角块求逆,跳过零块并保持其为零,避免全矩阵运算,时间复杂度降至o(n),适用于n≈10⁴量级场景。 在科学计算与算子建模中,常遇到结构稀疏的分块对角矩阵——例如由物...

如何在 Python 中高效求解模 2 下的行向量线性相关关系

本文介绍使用galois库在有限域gf(2)上快速计算矩阵零空间,从而自动找出使行向量线性组合模2为零的一组非零系数,避免手动实现高斯消元。 本文介绍使用galois库在有限域gf(2)上快速计算矩阵零空间,从而自动找出使行向量线性组合模2为零的一组非零系数,避免手动实现高斯消元。 在密码学、编码理论和布尔函数分析等场景中,常需判断一组整数向量在模2意义下是否线性相关,并显式构造一个非平凡的线性依...

Python 中随机生成符合约束条件的手术排程表:避免列表引用与重复追加陷阱

本文详解如何在python中正确实现带约束条件的随机排程生成逻辑,重点解决因列表引用误用和重复追加导致的无限循环问题,并提供可复用的健壮初始化方案。 本文详解如何在python中正确实现带约束条件的随机排程生成逻辑,重点解决因列表引用误用和重复追加导致的无限循环问题,并提供可复用的健壮初始化方案。 在医院手术排程优化问题中,一个常见约束是:任意时间槽(slot)内,跨三个手术室(theatre)所...

PuLP 教程:如何约束每项任务仅由一名员工执行(使用二元变量与大M法)

本文详解如何在pulp中建模“每项任务最多分配给一名员工”的逻辑约束——通过引入二元分配变量和大m法将非线性条件(如x>0)转化为线性整数规划约束。 本文详解如何在pulp中建模“每项任务最多分配给一名员工”的逻辑约束——通过引入二元分配变量和大m法将非线性条件(如x>0)转化为线性整数规划约束。 在资源分配类优化问题中,一个常见但易被忽视的关键业务规则是:某项具体任务(action)...