如何在Gurobi求解器中最小化价格/权重比?附报错及代码
运输价格/权重比优化问题的Gurobi求解错误修复
问题概述
优化目标为降低产品运输的价格/权重比,涉及核心变量:
order:待配送订单的重量向量w:每辆卡车的载重上限向量p:每辆卡车的运输成本向量n:单条配送路线的最大客户数量
报错分析
错误信息
TypeError Traceback (most recent call last) src/gurobipy/var.pxi in gurobipy.Var.__truediv__() TypeError: float() argument must be a string or a number, not 'generator' During handling of the above exception, another exception occurred: GurobiError Traceback (most recent call last) <ipython-input-5-18654dbac5a8> in <module> ---> 30 m.setObjective( gp.quicksum((z_n[i]/(gp.quicksum(x[i][j] *order[j]) for j in range(numItems))) for i in range(numTrucks)) , GRB.MINIMIZE) src/gurobipy/gurobi.pxi in gurobipy.quicksum() <ipython-input-5-18654dbac5a8> in <genexpr>(.0) ---> 30 m.setObjective( gp.quicksum((z_n[i]/(gp.quicksum(x[i][j] *order[j]) for j in range(numItems))) for i in range(numTrucks)) , GRB.MINIMIZE) src/gurobipy/var.pxi in gurobipy.Var.__truediv__() GurobiError: Divisor must be a constant
错误原因
- 语法错误:目标函数中
gp.quicksum(x[i][j] *order[j] for j in range(numItems))被额外括号包裹,生成了生成器对象而非Gurobi表达式,触发TypeError。 - 核心约束限制:Gurobi不允许目标函数中出现变量除以变量的形式(属于非凸非线性规划),且要求除数必须为常数;同时原代码未处理卡车未使用时总重量为0的除以0风险。
解决方案
建模调整
- 添加二进制变量
y[i],标记卡车i是否被使用。 - 修改成本变量
z_n[i]的约束,仅当卡车被使用时(y[i]=1),z_n[i]等于对应运输成本p[i]。 - 添加约束:未使用的卡车不能装载任何订单;使用的卡车装载总重量需大于一个极小值(避免除以0)。
- 启用Gurobi的
NonConvex参数,允许求解非线性目标函数。
修改后的代码
w = [650,1200,1200,1200,1200,1800,1800,1800,1800] p = [250,330, 330, 330, 330, 400, 400, 400,400] n = 7 order = [288,61,91,65,103,114,71,392,80,306,749,159,149,204,64,45,156,110,562,96,295,75,465,51,54,414,52,571,73,286,96,325,81,111,138,13,105,112,60,52,70,238,519,305,117,222,112,26,54,26,131,73,63,249,144,44,15,275,302] numTrucks = len(w) numItems = len(order) import gurobipy as gp from gurobipy import GRB m = gp.Model('trucks optimization') # 决策变量 x = m.addVars(numTrucks, numItems, vtype=gp.GRB.BINARY, name="x") # x[i,j]表示卡车i是否装载订单j y = m.addVars(numTrucks, vtype=gp.GRB.BINARY, name="y") # y[i]表示是否使用卡车i z_n = m.addVars(numTrucks, vtype=gp.GRB.CONTINUOUS, name="z_n") # 卡车i的运输成本 # 约束条件 # 1. 每辆卡车装载的订单数不超过n m.addConstrs(gp.quicksum(x[i,j] for j in range(numItems)) <= n * y[i] for i in range(numTrucks)) # 2. 每个订单必须被恰好一辆卡车装载 m.addConstrs(gp.quicksum(x[i,j] for i in range(numTrucks)) == 1 for j in range(numItems)) # 3. 每辆卡车的总装载重量不超过载重上限 m.addConstrs(gp.quicksum(x[i,j] * order[j] for j in range(numItems)) <= w[i] * y[i] for i in range(numTrucks)) # 4. 使用卡车时,运输成本等于p[i],否则为0 m.addConstrs(z_n[i] == p[i] * y[i] for i in range(numTrucks)) # 5. 使用卡车时,总装载重量至少为1(避免除以0) m.addConstrs(gp.quicksum(x[i,j] * order[j] for j in range(numItems)) >= 1 * y[i] for i in range(numTrucks)) # 修正目标函数:去掉多余括号,处理非线性 # 目标是最小化各卡车(运输成本/运输重量)之和 truck_weight = {i: gp.quicksum(x[i,j] * order[j] for j in range(numItems)) for i in range(numTrucks)} m.setObjective(gp.quicksum(z_n[i] / truck_weight[i] for i in range(numTrucks)), GRB.MINIMIZE) # 启用非线性求解 m.setParam('NonConvex', 2) m.optimize() # 输出结果 if m.status == GRB.OPTIMAL: print("最优解:") for i in range(numTrucks): if y[i].x > 0.5: used_weight = sum(x[i,j].x * order[j] for j in range(numItems)) print(f"卡车{i}:使用,成本{p[i]},装载重量{used_weight:.2f},价格/权重比{p[i]/used_weight:.4f}") else: print("未找到最优解")
替代方案(线性分式规划)
若目标为全局价格/权重比最小(总运输成本/总运输重量),可通过Charnes-Cooper变换转化为线性规划:
w = [650,1200,1200,1200,1200,1800,1800,1800,1800] p = [250,330, 330, 330, 330, 400, 400, 400,400] n = 7 order = [288,61,91,65,103,114,71,392,80,306,749,159,149,204,64,45,156,110,562,96,295,75,465,51,54,414,52,571,73,286,96,325,81,111,138,13,105,112,60,52,70,238,519,305,117,222,112,26,54,26,131,73,63,249,144,44,15,275,302] numTrucks = len(w) numItems = len(order) import gurobipy as gp from gurobipy import GRB m = gp.Model('trucks_global_ratio') # 决策变量 x = m.addVars(numTrucks, numItems, vtype=gp.GRB.BINARY, name="x") y = m.addVars(numTrucks, vtype=gp.GRB.BINARY, name="y") t = m.addVar(vtype=gp.GRB.CONTINUOUS, name="t", lb=1e-6) # 变换变量,t=1/总运输重量 # 约束条件 m.addConstrs(gp.quicksum(x[i,j] for j in range(numItems)) <= n * y[i] for i in range(numTrucks)) m.addConstrs(gp.quicksum(x[i,j] for i in range(numTrucks)) == 1 for j in range(numItems)) m.addConstrs(gp.quicksum(x[i,j] * order[j] for j in range(numItems)) <= w[i] * y[i] for i in range(numTrucks)) # Charnes-Cooper变换约束 total_weight = gp.quicksum(x[i,j] * order[j] for i in range(numTrucks) for j in range(numItems)) m.addConstr(total_weight * t == 1) total_cost = gp.quicksum(p[i] * y[i] for i in range(numTrucks)) # 目标转化为最小化总成本*t(等价于总成本/总重量) m.setObjective(total_cost * t, GRB.MINIMIZE) m.optimize() if m.status == GRB.OPTIMAL: total_weight_val = 1 / t.x total_cost_val = sum(p[i] * y[i].x for i in range(numTrucks)) print(f"全局最优价格/权重比:{total_cost_val / total_weight_val:.4f}") print("卡车使用情况:") for i in range(numTrucks): if y[i].x > 0.5: used_weight = sum(x[i,j].x * order[j] for j in range(numItems)) print(f"卡车{i}:装载重量{used_weight:.2f}")
内容的提问来源于stack exchange,提问作者Fernanda Almeida
相关产品推荐
相关产品推荐

