两阶段取送车辆路径问题CPLEX代码结构优化咨询
两阶段取送车辆路径问题CPLEX代码优化方案
一、优化后的代码结构
集合定义(简化命名+合并逻辑)
{string} Depots = ...; {string} Satellites = ...; {string} Customers = ...; {string} AllNodes = Depots union Satellites union Customers; {string} Tier1Nodes = Depots union Satellites; // 第一梯队节点:仓库+卫星点 {string} Tier2Nodes = Satellites union Customers; // 第二梯队节点:卫星点+客户
参数定义(统一命名风格)
float distance[AllNodes][AllNodes] = ...; // 全节点距离矩阵,仓库-客户直接连接成本高 float deliveryDemand[Customers] = ...; // 客户配送需求 float pickupDemand[Customers] = ...; // 客户取货需求 float satelliteCapacity[Satellites] = ...; // 卫星点容量 float bigVehicleCapacity = ...; // 第一梯队车辆容量 float smallVehicleCapacity = ...; // 第二梯队车辆容量
决策变量(分组+语义化命名)
// 路径弧变量 dvar boolean tier1Arc[Tier1Nodes][Tier1Nodes]; // 第一梯队路径弧 dvar boolean tier2Arc[Tier2Nodes][Tier2Nodes]; // 第二梯队路径弧 dvar boolean customerToSat[Customers][Satellites]; // 客户-卫星点分配关系 // 卫星点需求变量 dvar float+ satelliteDelivery[Satellites]; // 卫星点总配送需求 dvar float+ satellitePickup[Satellites]; // 卫星点总取货需求 // 车辆载货量辅助变量 dvar float+ tier1DeliverLoadBefore[Satellites]; // 第一梯队服务卫星点前配送载货量 dvar float+ tier1PickupLoadAfter[Satellites]; // 第一梯队服务卫星点后取货载货量 dvar float+ tier2DeliverLoadBefore[Customers]; // 第二梯队服务客户前配送载货量 dvar float+ tier2PickupLoadAfter[Customers]; // 第二梯队服务客户后取货载货量
目标函数(保持简洁)
// 最小化总运输成本 minimize sum(i,j in Tier1Nodes) distance[i][j] * tier1Arc[i][j] + sum(l,m in Tier2Nodes) distance[l][m] * tier2Arc[l][m];
约束条件(分组+简化重复逻辑)
subject to { // -------------------------- 第一梯队路径约束 -------------------------- // 卫星点必访问一次 forall(s in Satellites){ sum(j in Tier1Nodes: j != s) tier1Arc[s][j] == 1; } // 流量平衡 forall(n in Tier1Nodes){ sum(j in Tier1Nodes) tier1Arc[n][j] == sum(j in Tier1Nodes) tier1Arc[j][n]; } // 配送子路径消除 forall(i,j in Satellites){ tier1DeliverLoadBefore[j] - tier1DeliverLoadBefore[i] + bigVehicleCapacity * tier1Arc[i][j] <= bigVehicleCapacity - satelliteDelivery[i]; } // 第一梯队配送载货量上下界 forall(s in Satellites){ satelliteDelivery[s] <= tier1DeliverLoadBefore[s] && tier1DeliverLoadBefore[s] <= bigVehicleCapacity; } // 取货子路径消除 forall(i,j in Satellites){ tier1PickupLoadAfter[i] - tier1PickupLoadAfter[j] + bigVehicleCapacity * tier1Arc[i][j] <= bigVehicleCapacity - satellitePickup[j]; } // 第一梯队取货载货量上下界 forall(s in Satellites){ satellitePickup[s] <= tier1PickupLoadAfter[s] && tier1PickupLoadAfter[s] <= bigVehicleCapacity; } // 第一梯队车辆总载货量限制 forall(s in Satellites){ tier1DeliverLoadBefore[s] + tier1PickupLoadAfter[s] - satelliteDelivery[s] <= bigVehicleCapacity; } // -------------------------- 卫星点容量与需求约束 -------------------------- // 卫星点总需求不超容量 forall(s in Satellites){ satelliteDelivery[s] + satellitePickup[s] <= satelliteCapacity[s]; } // 卫星点取货需求等于分配客户的取货总和 forall(s in Satellites){ sum(c in Customers) customerToSat[c][s] * pickupDemand[c] == satellitePickup[s]; } // 卫星点配送需求等于分配客户的配送总和 forall(s in Satellites){ sum(c in Customers) customerToSat[c][s] * deliveryDemand[c] == satelliteDelivery[s]; } // -------------------------- 第二梯队路径约束 -------------------------- // 客户必访问一次 forall(c in Customers){ sum(m in Tier2Nodes: m != c) tier2Arc[c][m] == 1; } // 流量平衡 forall(n in Tier2Nodes){ sum(m in Tier2Nodes) tier2Arc[n][m] == sum(m in Tier2Nodes) tier2Arc[m][n]; } // -------------------------- 客户-卫星点分配约束 -------------------------- // 每个客户仅分配至一个卫星点 forall(c in Customers){ sum(s in Satellites) customerToSat[c][s] == 1; } // 非法路径规避:客户-卫星点弧仅在分配关系存在时允许 forall(s in Satellites, c in Customers){ tier2Arc[c][s] <= customerToSat[c][s]; // 客户到卫星点 tier2Arc[s][c] <= customerToSat[c][s]; // 卫星点到客户 } // 客户路径必须归属同一卫星点 forall(c1,c2 in Customers: c1 != c2, s in Satellites){ tier2Arc[c1][c2] + customerToSat[c1][s] + sum(s2 in Satellites: s2 != s) customerToSat[c2][s2] <= 2; } // -------------------------- 第二梯队载货量约束 -------------------------- // 配送子路径消除 forall(c1,c2 in Customers){ tier2DeliverLoadBefore[c2] - tier2DeliverLoadBefore[c1] + smallVehicleCapacity * tier2Arc[c1][c2] + (smallVehicleCapacity - deliveryDemand[c1] - deliveryDemand[c2]) * tier2Arc[c2][c1] <= smallVehicleCapacity - deliveryDemand[c1]; } // 第二梯队配送载货量下界 forall(c in Customers){ sum(m in Customers) tier2Arc[c][m] * deliveryDemand[m] + deliveryDemand[c] <= tier2DeliverLoadBefore[c]; } // 第二梯队配送载货量上界 forall(s in Satellites, c in Customers){ tier2DeliverLoadBefore[c] <= smallVehicleCapacity - (smallVehicleCapacity - deliveryDemand[c]) * tier2Arc[c][s]; } // 取货子路径消除 forall(c1,c2 in Customers){ tier2PickupLoadAfter[c1] - tier2PickupLoadAfter[c2] + smallVehicleCapacity * tier2Arc[c1][c2] + (smallVehicleCapacity - pickupDemand[c1] - pickupDemand[c2]) * tier2Arc[c2][c1] <= smallVehicleCapacity - pickupDemand[c2]; } // 第二梯队取货载货量下界 forall(c in Customers){ sum(m in Customers) tier2Arc[m][c] * pickupDemand[m] + pickupDemand[c] <= tier2PickupLoadAfter[c]; } // 第二梯队取货载货量上界 forall(s in Satellites, c in Customers){ tier2PickupLoadAfter[c] <= smallVehicleCapacity - (smallVehicleCapacity - pickupDemand[c]) * tier2Arc[s][c]; } // 第二梯队车辆总载货量限制 forall(c in Customers){ tier2DeliverLoadBefore[c] + tier2PickupLoadAfter[c] - deliveryDemand[c] <= smallVehicleCapacity; } }
二、核心优化点说明
1. 集合与命名优化
- 用语义化命名替代原有的模糊命名,比如
Tier1Nodes、tier1Arc,直接体现变量功能,提升代码可读性 - 集合定义保留必要的合并逻辑,同时通过命名明确各集合的覆盖范围,避免歧义
2. 代码结构模块化
- 将约束按功能分组(第一梯队、卫星点、第二梯队、分配关系、载货量),用注释分隔,便于后续维护和调试
- 合并嵌套循环:比如原约束15、16的双重循环合并为单个
forall循环,简化代码结构的同时保持逻辑不变
3. 决策变量精简
- 对辅助变量按功能分组命名,明确区分不同梯队的载货量变量,避免混淆
- 在保留必要变量的前提下,去掉冗余的命名重复,提升代码简洁性
4. 约束逻辑简化
- 合并重复的约束结构,比如客户-卫星点的双向弧约束,用一次循环处理两种方向的弧限制
- 调整代码排版,让约束逻辑更直观,降低理解成本
内容的提问来源于stack exchange,提问作者Felix Miller
相关产品推荐
相关产品推荐

