如何使用Python将单地点线性规划(LP)模型扩展到多地点场景
多点位退货资源分配问题解决方案
你遇到的KeyError本质是多维度变量创建和访问时索引不匹配导致的,同时因为你的点位之间无耦合约束,有两种实现方案可选:
方案1:循环调用单点位模型(最简单,无额外调试成本)
因为每个点位的设备部署、成本计算完全独立,没有全局设备数量限制、跨点位运力调度等耦合约束,直接循环遍历所有点位运行你已有的单点位代码即可,完全不需要修改模型结构:
import gurobipy as gp from gurobipy import GRB # 输入参数:所有点位的退货量列表 locations = [120000, 850000, 300000] # 示例数据,替换成你的实际数据 result = [] for i, q in enumerate(locations): # 直接复用你原来的单点位代码 m = gp.Model(f"point_{i}") BigM = 100 cost_A = 20000 cost_B = 13500 cost_transport = 0.007 cap_A = 400000 cap_B = 500000 x = m.addVar(lb=0, ub=3, vtype=GRB.INTEGER, name="A") y = m.addVar(lb=0, ub=3, vtype=GRB.INTEGER, name="B") z = m.addVar(lb=0, vtype=GRB.INTEGER, name="flow_1") a = m.addVar(vtype=GRB.BINARY, name="switch") m.setObjective(cost_A * x + cost_B * y + cost_transport * z, GRB.MINIMIZE) m.addConstr(z == q, "accept_demand") m.addConstr(z <= cap_A * x + cap_B * y, "capacity") m.addConstr(y <= x, "B_le_A") m.addConstr(x - y <= BigM * a, "switch_1") m.addConstr(y <= BigM * (1 - a), "switch_2") # 关闭日志输出避免刷屏 m.setParam('OutputFlag', 0) m.optimize() # 保存结果 result.append({ "点位id": i, "退货量": q, "A设备数量": x.X, "B设备数量": y.X, "总成本": m.ObjVal }) # 打印所有结果 for res in result: print(res)
这个方案完全规避了多变量索引的问题,调试成本最低,运行效率也足够应对大多数点位规模场景。
方案2:整合式多点位MIP模型(适合后续要加全局约束的场景)
如果你后续需要加全局约束(比如总A设备不能超过10台、总成本上限等),可以把所有点位整合到一个模型里,正确的多变量创建和约束写法如下:
import gurobipy as gp from gurobipy import GRB # 输入参数 locations = [120000, 850000, 300000] # 示例数据 I = range(len(locations)) # 点位索引集合 BigM = 100 cost_A = 20000 cost_B = 13500 cost_transport = 0.007 cap_A = 400000 cap_B = 500000 # 创建模型 m = gp.Model("multi_point_return") # 按点位索引创建变量,所有变量都是单维度i索引 x = m.addVars(I, lb=0, ub=3, vtype=GRB.INTEGER, name="A") y = m.addVars(I, lb=0, ub=3, vtype=GRB.INTEGER, name="B") z = m.addVars(I, lb=0, vtype=GRB.INTEGER, name="flow") a = m.addVars(I, vtype=GRB.BINARY, name="switch") # 目标函数:所有点位成本总和 m.setObjective( gp.quicksum(cost_A * x[i] + cost_B * y[i] + cost_transport * z[i] for i in I), GRB.MINIMIZE ) # 批量加约束,每个点位一套约束 m.addConstrs((z[i] == locations[i] for i in I), "accept_demand") m.addConstrs((z[i] <= cap_A * x[i] + cap_B * y[i] for i in I), "capacity") m.addConstrs((y[i] <= x[i] for i in I), "B_le_A") m.addConstrs((x[i] - y[i] <= BigM * a[i] for i in I), "switch_1") m.addConstrs((y[i] <= BigM * (1 - a[i]) for i in I), "switch_2") # 可选:添加全局约束示例,比如总A设备不超过5台 # m.addConstr(gp.quicksum(x[i] for i in I) <= 5, "total_A_limit") # 求解 m.optimize() # 输出结果 for i in I: print(f"点位{i}: A={x[i].X}, B={y[i].X}, 退货量={locations[i]}, 成本={cost_A * x[i].X + cost_B * y[i].X + cost_transport * locations[i]}")
KeyError规避说明
只要你严格按照定义的索引维度访问变量就不会报错:
- 变量定义时用
addVars(I, ...),I是单维度集合,访问时就用x[i],不要多写索引(比如x[i,j])也不要漏索引(比如直接用x参与计算) - 确保你访问时用的i都在I的范围内,不要用超过点位数量的索引值
非Gurobi实现方案
因为该问题每个点位的可选方案非常有限(A设备最多3台,B只能取0或等于A的数量),完全可以用穷举法计算所有可行方案的成本取最小值,不需要调用整数规划求解器:
def calc_best_plan(q): cost_A = 20000 cost_B = 13500 cost_transport = 0.007 cap_A = 400000 cap_B = 500000 min_cost = float('inf') best_x = 0 best_y = 0 # 遍历所有可行的x(0-3) for x in range(0,4): # B只能取0或者x for y in [0, x]: # 校验容量是否满足 if cap_A * x + cap_B * y >= q: total_cost = cost_A * x + cost_B * y + cost_transport * q if total_cost < min_cost: min_cost = total_cost best_x = x best_y = y return best_x, best_y, min_cost # 调用示例 locations = [120000, 850000, 300000] for i, q in enumerate(locations): x,y,cost = calc_best_plan(q) print(f"点位{i}: A={x}, B={y}, 总成本={cost}")
该方案运行速度极快,不需要安装任何求解器依赖,适合小范围可行域的规划场景。
内容的提问来源于stack exchange,提问作者Bas van Rooij
相关产品推荐
相关产品推荐

