基于PuLP的运输优化:如何融入运输量折扣机制
解决方案:在PuLP中实现运输量折扣的目标函数
要实现基于仓库总运输量的折扣规则,需要引入二进制变量标记是否满足折扣条件,通过约束将变量与运输量关联,最后构建包含折扣逻辑的线性目标函数。以下是具体实现步骤和完整代码:
1. 定义折扣标记变量
添加两个二进制变量,分别表示仓库B、C是否享受折扣:
# 二进制变量:1表示享受折扣,0表示不享受 disc_B = LpVariable("Discount_B", cat=LpBinary) disc_C = LpVariable("Discount_C", cat=LpBinary)
2. 添加折扣触发约束
通过约束确保折扣仅在运输量达标时生效:
- 仓库B总运输量≥200时,
disc_B必须为1;否则为0 - 仓库C总运输量≥600时,
disc_C必须为1;否则为0
# 仓库B的折扣约束 total_ship_B = lpSum(vars["B"][j] for j in projects) # 若disc_B=1,总运输量≥200;若disc_B=0,约束自动满足(左边≥0) prob += total_ship_B >= 200 * disc_B, f"Disc_B_Min_Shipment" # 若disc_B=0,总运输量≤199;若disc_B=1,约束上限为仓库B的供应量(600) prob += total_ship_B <= 199 + (supply["B"] - 199) * disc_B, f"Disc_B_Max_Shipment" # 仓库C的折扣约束 total_ship_C = lpSum(vars["C"][j] for j in projects) prob += total_ship_C >= 600 * disc_C, f"Disc_C_Min_Shipment" prob += total_ship_C <= 599 + (supply["C"] - 599) * disc_C, f"Disc_C_Max_Shipment"
3. 构建带折扣的目标函数
目标函数由基础总成本减去折扣金额组成,折扣仅在满足条件时生效:
# 计算无折扣的基础总成本 base_cost = lpSum([vars[w][j] * costs[w][j] for w in warehouses for j in projects]) # 仓库B的折扣金额:仅当disc_B=1时,减去5%的B路径总成本 discount_B = 0.05 * lpSum([vars["B"][j] * costs["B"][j] for j in projects]) * disc_B # 仓库C的折扣金额:仅当disc_C=1时,减去10%的C路径总成本 discount_C = 0.1 * lpSum([vars["C"][j] * costs["C"][j] for j in projects]) * disc_C # 最终目标函数:基础成本减去折扣金额 prob += base_cost - discount_B - discount_C, "Total_Transport_Cost"
4. 补充核心约束
原代码缺少供应和需求约束,必须添加以保证模型合理性:
# 供应约束:每个仓库的总发货量不超过其供应量 for w in warehouses: prob += lpSum(vars[w][j] for j in projects) <= supply[w], f"Supply_Limit_{w}" # 需求约束:每个项目的总收货量等于其需求量 for j in projects: prob += lpSum(vars[w][j] for w in warehouses) == demand[j], f"Demand_Satisfaction_{j}"
完整修改后代码
# Creates a list of all the supply nodes warehouses = ["A", "B", "C"] # Creates a dictionary for the number of units of supply for each supply node supply = {"A": 150, "B": 600, "C":1000} # Creates a list of all demand nodes projects = ["1", "2", "3"] # Creates a dictionary for the number of units of demand for each demand node demand = { "1": 150, "2": 450, "3": 900, } # Creates a list of costs of each transportation path costs = [ # Projects [5,1,9], # A warehouses [4,2,8], # B [8,7,2] # C ] # The cost data is made into a dictionary costs = makeDict([warehouses, projects], costs, 0) # Import PuLP modeler functions from pulp import * # Creates the 'prob' variable to contain the problem data prob = LpProblem("Material Supply Problem", LpMinimize) # Creates a list of tuples containing all the possible routes for transport Routes = [(w, b) for w in warehouses for b in projects] # A dictionary called 'Vars' is created to contain the referenced variables(the routes) vars = LpVariable.dicts("Route", (warehouses, projects), 0, None, LpInteger) # -------------------------- 新增折扣相关部分 -------------------------- # 二进制变量:标记是否享受折扣 disc_B = LpVariable("Discount_B", cat=LpBinary) disc_C = LpVariable("Discount_C", cat=LpBinary) # 仓库B的折扣触发约束 total_ship_B = lpSum(vars["B"][j] for j in projects) prob += total_ship_B >= 200 * disc_B, f"Disc_B_Min_Shipment" prob += total_ship_B <= 199 + (supply["B"] - 199) * disc_B, f"Disc_B_Max_Shipment" # 仓库C的折扣触发约束 total_ship_C = lpSum(vars["C"][j] for j in projects) prob += total_ship_C >= 600 * disc_C, f"Disc_C_Min_Shipment" prob += total_ship_C <= 599 + (supply["C"] - 599) * disc_C, f"Disc_C_Max_Shipment" # 构建带折扣的目标函数 base_cost = lpSum([vars[w][j] * costs[w][j] for w in warehouses for j in projects]) discount_B = 0.05 * lpSum([vars["B"][j] * costs["B"][j] for j in projects]) * disc_B discount_C = 0.1 * lpSum([vars["C"][j] * costs["C"][j] for j in projects]) * disc_C prob += base_cost - discount_B - discount_C, "Total_Transport_Cost" # -------------------------- 折扣部分结束 -------------------------- # 补充供应与需求约束 for w in warehouses: prob += lpSum(vars[w][j] for j in projects) <= supply[w], f"Supply_Limit_{w}" for j in projects: prob += lpSum(vars[w][j] for w in warehouses) == demand[j], f"Demand_Satisfaction_{j}" # 求解模型 prob.solve() # 打印结果 print("Status:", LpStatus[prob.status]) for v in prob.variables(): if v.varValue > 0: print(f"{v.name}: {v.varValue}") print(f"Total Cost: ${value(prob.objective):.2f}")
逻辑说明
- 二进制变量
disc_B和disc_C相当于"开关",只有当仓库总运输量满足阈值时才会被激活为1 - 约束条件确保"开关"与运输量严格关联:不满足阈值时开关必须为0,满足时必须为1
- 目标函数通过线性组合实现折扣计算,完全符合PuLP的线性规划要求(二进制变量与连续变量的乘积属于线性项)
内容的提问来源于stack exchange,提问作者Vasuki Rao
相关产品推荐
相关产品推荐

