|
| 1 | +import gurobipy |
| 2 | +from gurobipy import GRB |
| 3 | +import numpy |
| 4 | + |
| 5 | +# https://zhuanlan.zhihu.com/p/651080846 |
| 6 | + |
| 7 | +# 边的定义和距离矩阵的初始化 |
| 8 | +edges = { |
| 9 | + (0, 1): 4, (0, 3): 3.5, (0, 5): 6.2, (0, 7): 3.5, (0, 8): 6, |
| 10 | + (1, 2): 3.7, (1, 4): 4.5, (1, 9): 3, |
| 11 | + (2, 3): 3.4, (2, 10): 5.9, |
| 12 | + (4, 5): 3.3, |
| 13 | + (5, 10): 7.2, (5, 11): 3, |
| 14 | + (6, 10): 7, (6, 12): 7.3, |
| 15 | + (7, 12): 8.8, (7, 14): 5.8, (7, 15): 3.4, |
| 16 | + (8, 11): 11, |
| 17 | + (9, 14): 4.5, |
| 18 | + (10, 12): 7.5, (10, 13): 3.1, |
| 19 | + (12, 13): 4.3, |
| 20 | + (13, 15): 8.4, |
| 21 | + (14, 15): 6.9 |
| 22 | +} |
| 23 | + |
| 24 | +distances = numpy.full((16, 16), GRB.INFINITY) |
| 25 | +for k, v in edges.items(): |
| 26 | + i, j = k |
| 27 | + distances[i][j] = v |
| 28 | + distances[j][i] = v |
| 29 | + |
| 30 | +numpy.fill_diagonal(distances, 0) # 自己到自己的距离为0 |
| 31 | + |
| 32 | +# 问题参数:客户数量和车辆数 |
| 33 | +n = 15 # 客户点数目 |
| 34 | +m = 3 # 车辆数目 |
| 35 | + |
| 36 | +# 创建优化模型 |
| 37 | +model = gurobipy.Model("VRP") |
| 38 | + |
| 39 | +# 决策变量:X[k, i, j] 表示车辆k是否从i点行驶到j点 |
| 40 | +X = model.addVars(m, 1 + n, 1 + n, vtype=GRB.BINARY, name='X') |
| 41 | + |
| 42 | +# 目标函数:最小化总行驶距离 |
| 43 | +objFunc = gurobipy.quicksum( |
| 44 | + distances[i, j] * X[k, i, j] |
| 45 | + for k in range(m) |
| 46 | + for i in range(n + 1) |
| 47 | + for j in range(n + 1) |
| 48 | +) |
| 49 | + |
| 50 | +model.setObjective(objFunc, GRB.MINIMIZE) |
| 51 | + |
| 52 | +# 约束条件:每辆车从起点出发 |
| 53 | +for k in range(m): |
| 54 | + model.addConstr(gurobipy.quicksum(X[k, 0, j] for j in range(1, n + 1)) == 1) |
| 55 | + |
| 56 | +# 约束条件:每辆车必须回到起点 |
| 57 | +for k in range(m): |
| 58 | + model.addConstr(gurobipy.quicksum(X[k, j, 0] for j in range(1, n + 1)) == 1) |
| 59 | + |
| 60 | +# 流量平衡约束:每个客户点的进出流量必须相同 |
| 61 | +for k in range(m): |
| 62 | + for j in range(1, n + 1): |
| 63 | + model.addConstr( |
| 64 | + gurobipy.quicksum(X[k, i, j] for i in range(n + 1) if i != j) == |
| 65 | + gurobipy.quicksum(X[k, j, i] for i in range(n + 1) if i != j), |
| 66 | + name='' |
| 67 | + ) |
| 68 | + |
| 69 | +# 访问所有客户点的约束:每个客户点必须被访问一次 |
| 70 | +for j in range(1, n + 1): |
| 71 | + model.addConstr( |
| 72 | + gurobipy.quicksum(X[k, i, j] for k in range(m) for i in range(n + 1) if i != j) == 1 |
| 73 | + ) |
| 74 | + |
| 75 | +# 禁止不可达的路径 |
| 76 | +for k in range(m): |
| 77 | + for i in range(n + 1): |
| 78 | + for j in range(n + 1): |
| 79 | + if distances[i, j] == GRB.INFINITY: |
| 80 | + model.addConstr(X[k, i, j] == 0) |
| 81 | + if i == j: |
| 82 | + model.addConstr(X[k, i, j] == 0) |
| 83 | + |
| 84 | +# https://zhuanlan.zhihu.com/p/159270139 |
| 85 | +# 子回路消除约束:基于 MTZ |
| 86 | +U = model.addVars(m, 1 + n, lb=0, ub=n, vtype=GRB.INTEGER, name='U') |
| 87 | + |
| 88 | +# MTZ 约束:防止车辆走子回路 |
| 89 | +for k in range(m): |
| 90 | + for i in range(1, n + 1): |
| 91 | + for j in range(1, n + 1): |
| 92 | + if i != j: |
| 93 | + model.addConstr(U[k, i] - U[k, j] + n * X[k, i, j] <= n - 1) |
| 94 | + |
| 95 | +# 起点顺序号为0 |
| 96 | +for k in range(m): |
| 97 | + model.addConstr(U[k, 0] == 0) |
| 98 | + |
| 99 | +# 更新模型并求解 |
| 100 | +model.update() |
| 101 | +model.optimize() |
| 102 | + |
| 103 | +# 检查是否找到最优解 |
| 104 | +if model.status == GRB.OPTIMAL: |
| 105 | + print(f"最小总行驶距离: {model.objVal}") |
| 106 | +else: |
| 107 | + print(f"没有找到最优解,状态码: {model.status}") |
| 108 | + |
| 109 | + model.computeIIS() |
| 110 | + model.write("model.lp") |
0 commit comments