Python代码实现
3.1 gurobi包的导入及参数设置
导入gurobi包,利用gurobi来构建模型并求解。
from gurobipy import *
输入构建模型所需数据,设置控制迭代过程的参数。
Arc, Cost = multidict({(1,2):1, #费用
(1,3):10,
(2,4):1,
(2,5):2,
(3,2):1,
(3,4):5,
(3,5):12,
(4,5):10,
(4,6):1,
(5,6):2,})
Time = {(1,2):10, #时间
(1,3):3,
(2,4):1,
(2,5):3,
(3,2):2,
(3,4):7,
(3,5):3,
(4,5):1,
(4,6):7,
(5,6):2,}
n = 6 #节点个数
T = 15 #约束最短路的时间限制
lam = 0 #初始拉格朗日乘子
UB = 20 #上界
beta = 0.1 #迭代步长的倍数
3.2 拉格朗日松弛模型构建
利用gurobi包来构建拉格朗日松弛模型并进行求解。
mm = Model("约束最短路")
x = mm.addVars(Arc, vtype = GRB.BINARY)
mm.setObjective(quicksum(Cost*x for i in Arc) + lam*(quicksum(Time*x for i in Arc)-T))
for i in range(1,n+1):
if i == 1:
mm.addConstr(quicksum(x for i in Arc.select(i,'*')) == 1)
elif i == n:
mm.addConstr(quicksum(x for i in Arc.select('*',i)) == 1)
else:
mm.addConstr(quicksum(x for i in Arc.select(i,'*'))-quicksum(x for i in Arc.select('*',i)) == 0)
mm.optimize()
3.3 使用次梯度法进行迭代
获取当前求得的下界值,并计算当前的次梯度。
DB = mm.objVal #获取当前下界值
subgradient = quicksum(Time*x.x for i in Arc) - T #计算次梯度
判断算法是否停止。
if subgradient.getValue() == 0: #若次梯度为0,则停止
break
if t >= 500: #若迭代次数达到500次,则停止
break
计算步长,更新
。
theta = beta * (UB - DB) / subgradient.getValue()**2 #计算步长
lam = max(0, lam + theta * subgradient.getValue()) #更新拉格朗日乘子
在更新
后重新求解拉格朗日松弛模型,并使迭代次数加1。
mm.setObjective(quicksum(Cost*x for i in Arc) + lam*(quicksum(Time*x for i in Arc)-T))
mm.optimize()
t=t+1
3.4 计算结果
迭代过程如下表所示:

获得最优解为(1,2,5,6),最小费用为5,总花费时间为15,满足时间要求。注意,当改变时间限制的取值时,可能无法直接得到原问题的最优解,而只能收敛到一个比较好的下界值。此时可以通过其他的方法(比如启发式算法)来得到可行解,并根据拉格朗日松弛得到的下界值来判断可行解的质量。
参考文献
[1] Held, M., & Karp, R. M. (1970). The traveling-salesman problem and minimum spanning trees. Operations Research, 18(6), 1138-1162.
[2] Held, M., & Karp, R. M. (1971). The traveling-salesman problem and minimum spanning trees: Part II. Mathematical programming, 1(1), 6-25.
[3] Bazaraa, M. S., Sherali, H. D., & Shetty, C. M. (2013). Nonlinear programming: theory and algorithms. John Wiley & Sons.
本文转载自微信公众号:【交通与优化】(ID:OR_Transportation)
作者:王鸿杨