OR-tools约束优化:如何实现快递包裹阶梯式计价规则?
OR-Tools阶梯计价约束优化解决方案
原代码核心问题
- 决策变量条件判断错误:目标函数中直接用
all_courier_sums[courier_num] >= 2这类Python条件判断是无效的——决策变量在模型求解前是未知的,无法通过Python分支逻辑处理。 - 变量关联缺失:
all_courier_sums(快递商承接包裹数)未与parcel_by_courier(包裹分配变量)建立约束关联,导致模型无法正确统计每个快递商的承接数量。 - 多件约束写法错误:原代码中用Python条件判断生成约束的方式逻辑混乱,应直接针对
courier_further_item_allowed为False的快递商添加承接数量≤1的约束。
二阶计价实现(首件+后续件)
以下是修正后的完整代码,实现「首件一个价格,后续所有件另一个价格」的计价规则:
def cost_optimisation(): # 初始化求解器 solver = pywraplp.Solver.CreateSolver('SCIP') # 获取数据 data = get_model_data() max_parcels = len(data['parcel_range']) ### 变量定义 ### # 包裹-快递商分配变量:1表示快递商承接该包裹,0表示不承接 parcel_by_courier = {} for parcel_num in data['parcel_range']: for courier_num in data['courier_range']: parcel_by_courier[parcel_num, courier_num] = solver.BoolVar(f"parcel_{parcel_num}_courier_{courier_num}") # 快递商承接总包裹数 all_courier_sums = {} for courier_num in data['courier_range']: all_courier_sums[courier_num] = solver.IntVar(0, max_parcels, f"{data['courier_names'][courier_num]}_total_parcels_shipped") # 辅助布尔变量:标记快递商是否承接至少1件包裹(用于首件计价触发) courier_has_parcel = {} for courier_num in data['courier_range']: courier_has_parcel[courier_num] = solver.BoolVar(f"courier_{courier_num}_has_parcel") print('变量总数 =', solver.NumVariables(), '\n') ### 约束定义 ### # 1. 所有包裹必须被分配给唯一快递商 for parcel_num in data['parcel_range']: solver.Add(sum(parcel_by_courier[parcel_num, courier_num] for courier_num in data['courier_range']) == 1) # 2. 关联快递商承接数量与包裹分配变量 for courier_num in data['courier_range']: solver.Add(all_courier_sums[courier_num] == sum(parcel_by_courier[parcel_num, courier_num] for parcel_num in data['parcel_range'])) # 3. 重量与尺寸约束:仅当快递商承接包裹时,约束生效 for parcel_num in data['parcel_range']: for courier_num in data['courier_range']: solver.Add(parcel_by_courier[parcel_num, courier_num] * data['parcel_weights'][parcel_num] <= data['courier_weights'][courier_num]) solver.Add(parcel_by_courier[parcel_num, courier_num] * data['parcel_largest_dimensions'][parcel_num] <= data['courier_largest_dimensions'][courier_num]) # 4. 限制不可承接多件的快递商最多承接1件 for courier_num in data['courier_range']: if not data['courier_further_item_allowed'][courier_num]: solver.Add(all_courier_sums[courier_num] <= 1) # 5. 辅助变量约束:courier_has_parcel=1 当且仅当 快递商承接至少1件 for courier_num in data['courier_range']: solver.Add(all_courier_sums[courier_num] >= courier_has_parcel[courier_num]) solver.Add(all_courier_sums[courier_num] <= max_parcels * courier_has_parcel[courier_num]) print('约束总数 =', solver.NumConstraints(), '\n') ### 目标函数 ### total_cost = solver.Sum( # 首件成本:仅当快递商承接包裹时收取 data['courier_first_item_pence'][courier_num] * courier_has_parcel[courier_num] + # 后续件成本:总件数减1(首件)乘以后续件单价 data['courier_further_item_pence'][courier_num] * (all_courier_sums[courier_num] - courier_has_parcel[courier_num]) for courier_num in data['courier_range'] ) solver.Minimize(total_cost) status = solver.Solve() # 输出结果 if status == solver.OPTIMAL: print('最优成本 =', solver.Objective().Value(), '便士') for courier_num in data['courier_range']: cnt = int(all_courier_sums[courier_num].solution_value()) if cnt > 0: print(f"快递商 {data['courier_names'][courier_num]} 承接 {cnt} 件包裹") for parcel_num in data['parcel_range']: if parcel_by_courier[parcel_num, courier_num].solution_value() == 1: print(f" - 包裹 {parcel_num}") else: print('未找到最优解')
三阶计价扩展(首件+第2-5件+第6件及以上)
如果需要实现三阶阶梯计价,只需新增辅助变量标记阶梯区间,并调整目标函数计算逻辑:
def cost_optimisation(): # 初始化求解器 solver = pywraplp.Solver.CreateSolver('SCIP') # 获取数据 data = get_model_data() max_parcels = len(data['parcel_range']) ### 变量定义 ### parcel_by_courier = {} for parcel_num in data['parcel_range']: for courier_num in data['courier_range']: parcel_by_courier[parcel_num, courier_num] = solver.BoolVar(f"parcel_{parcel_num}_courier_{courier_num}") all_courier_sums = {} for courier_num in data['courier_range']: all_courier_sums[courier_num] = solver.IntVar(0, max_parcels, f"{data['courier_names'][courier_num]}_total_parcels_shipped") # 辅助布尔变量:标记不同阶梯区间 courier_has_parcel = {} # 至少1件(首件阶梯) courier_has_mid = {} # 至少2件(第2-5件阶梯) courier_has_high = {} # 至少6件(第6件及以上阶梯) for courier_num in data['courier_range']: courier_has_parcel[courier_num] = solver.BoolVar(f"courier_{courier_num}_has_parcel") courier_has_mid[courier_num] = solver.BoolVar(f"courier_{courier_num}_has_mid") courier_has_high[courier_num] = solver.BoolVar(f"courier_{courier_num}_has_high") print('变量总数 =', solver.NumVariables(), '\n') ### 约束定义 ### # 1. 所有包裹必须分配 for parcel_num in data['parcel_range']: solver.Add(sum(parcel_by_courier[parcel_num, courier_num] for courier_num in data['courier_range']) == 1) # 2. 关联承接数量与包裹分配变量 for courier_num in data['courier_range']: solver.Add(all_courier_sums[courier_num] == sum(parcel_by_courier[parcel_num, courier_num] for parcel_num in data['parcel_range'])) # 3. 重量与尺寸约束 for parcel_num in data['parcel_range']: for courier_num in data['courier_range']: solver.Add(parcel_by_courier[parcel_num, courier_num] * data['parcel_weights'][parcel_num] <= data['courier_weights'][courier_num]) solver.Add(parcel_by_courier[parcel_num, courier_num] * data['parcel_largest_dimensions'][parcel_num] <= data['courier_largest_dimensions'][courier_num]) # 4. 单包裹限制 for courier_num in data['courier_range']: if not data['courier_further_item_allowed'][courier_num]: solver.Add(all_courier_sums[courier_num] <= 1) # 5. 辅助变量约束 for courier_num in data['courier_range']: # 至少1件 solver.Add(all_courier_sums[courier_num] >= courier_has_parcel[courier_num]) solver.Add(all_courier_sums[courier_num] <= max_parcels * courier_has_parcel[courier_num]) # 至少2件(必须先满足至少1件) solver.Add(all_courier_sums[courier_num] >= 2 * courier_has_mid[courier_num]) solver.Add(all_courier_sums[courier_num] <= max_parcels * courier_has_mid[courier_num] + 1 * (1 - courier_has_mid[courier_num])) solver.Add(courier_has_mid[courier_num] <= courier_has_parcel[courier_num]) # 至少6件(必须先满足至少2件) solver.Add(all_courier_sums[courier_num] >= 6 * courier_has_high[courier_num]) solver.Add(all_courier_sums[courier_num] <= max_parcels * courier_has_high[courier_num] + 5 * (1 - courier_has_high[courier_num])) solver.Add(courier_has_high[courier_num] <= courier_has_mid[courier_num]) print('约束总数 =', solver.NumConstraints(), '\n') ### 目标函数 ### total_cost = solver.Sum( # 首件成本 data['courier_first_item_pence'][courier_num] * courier_has_parcel[courier_num] + # 第2-5件成本:最多4件,计算总件数减首件后,减去超过5件的部分 data['courier_mid_item_pence'][courier_num] * ( (all_courier_sums[courier_num] - courier_has_parcel[courier_num]) - solver.Max(0, all_courier_sums[courier_num] - 5) ) + # 第6件及以上成本:总件数减5后的正数部分 data['courier_high_volume_item_pence'][courier_num] * solver.Max(0, all_courier_sums[courier_num] - 5) for courier_num in data['courier_range'] ) solver.Minimize(total_cost) status = solver.Solve() # 输出结果 if status == solver.OPTIMAL: print('最优成本 =', solver.Objective().Value(), '便士') for courier_num in data['courier_range']: cnt = int(all_courier_sums[courier_num].solution_value()) if cnt > 0: print(f"快递商 {data['courier_names'][courier_num]} 承接 {cnt} 件包裹") for parcel_num in data['parcel_range']: if parcel_by_courier[parcel_num, courier_num].solution_value() == 1: print(f" - 包裹 {parcel_num}") else: print('未找到最优解')
内容的提问来源于stack exchange,提问作者Ana
相关产品推荐
相关产品推荐

