【MDVRP】Python+Gurobi求解运输标题建模实践四

打印 上一主题 下一主题

主题 552|帖子 552|积分 1656

本日的主题是分享Python调用运筹优化求解器(Gurobi)求解VRP扩展标题之MDVRP标题的教程。
  
  
1. 模型

1.1 MDVRP标题先容

MDVRP 作为 VRP 研究的一个扩展标题,重要是针对有多个货物中转点运输的场景。相比于单车场标题,多车场标题需要解决客户需求分配、车辆运输路径选择、车辆运输模式、车场货物容量等一系列标题。

1.2 数学模型


2. 数据布局

(1)demand文件布局

(2)depot文件布局

3. Gurobi源码

  1. import math
  2. import csv
  3. import copy
  4. import xlsxwriter
  5. import matplotlib.pyplot as plt
  6. from gurobipy import quicksum,Model,GRB
  7. # 读取文件
  8. def read_csv_file(demand_file,depot_file):
  9.     """
  10.     :param demand_file: 需求文件
  11.     :param depot_file: 车场文件
  12.     :return:
  13.     """
  14.     I = []
  15.     J = []
  16.     Q = {}
  17.     C = {}
  18.     XY = {}
  19.     with open(demand_file, 'r') as f:
  20.         demand_reader = csv.DictReader(f)
  21.         for row in demand_reader:
  22.             I.append(row['id'])
  23.             Q[row['id']] = float(row['demand'])
  24.             XY[row['id']] = (float(row['x_coord']), float(row['y_coord']))
  25.     with open(depot_file, 'r') as f:
  26.         depot_reader = csv.DictReader(f)
  27.         for row in depot_reader:
  28.             J.append(row['id'])
  29.             XY[row['id']] = (float(row['x_coord']), float(row['y_coord']))
  30.     N = I + J
  31.     for i in N:
  32.         x1, y1 = XY[i][0], XY[i][1]
  33.         for j in N:
  34.             x2, y2 = XY[j][0], XY[j][1]
  35.             C[i, j] = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
  36.     return N,I,J,C,Q,XY
  37. # 提取路径
  38. def extract_routes(I,J,X,K):
  39.     I = copy.deepcopy(I)
  40.     route_list = []
  41.     for k in K:
  42.         # 提取 派送阶段路径
  43.         cur_node = None
  44.         for j in J:
  45.             for i in I:
  46.                 if X[j, i,k].x > 0:
  47.                     cur_node = i
  48.                     route = [j,i]
  49.                     I.remove(i)
  50.                     break
  51.         if cur_node is None:
  52.             continue
  53.         while cur_node not in J:
  54.             for i in I+J:
  55.                 if X[cur_node, i, k].x > 0:
  56.                     cur_node = i
  57.                     route.append(i)
  58.                     if i not in J:
  59.                         I.remove(i)
  60.                     break
  61.         route_list.append(route)
  62.     return route_list
  63. def draw_routes(route_list,XY,I,J):
  64.     for route in route_list:
  65.         path_x = []
  66.         path_y = []
  67.         for n in route:
  68.             path_x.append(XY[n][0])
  69.             path_y.append(XY[n][1])
  70.         plt.plot(path_x, path_y, ms=5)
  71.     demand_point_x = [XY[n][0] for n in I]
  72.     demand_point_y = [XY[n][1] for n in I]
  73.     depot_point_x = [XY[n][0] for n in J]
  74.     depot_point_y = [XY[n][1] for n in J]
  75.     plt.scatter( demand_point_x, demand_point_y, marker='s', c='b', s=30,zorder=0)
  76.     plt.scatter( depot_point_x, depot_point_y, marker='*', c='r', s=100,zorder=1)
  77.     plt.show()
  78. # 保存结果
  79. def save_file(route_list,total_cost,C):
  80.     wb = xlsxwriter.Workbook('路径方案.xlsx')
  81.     ws = wb.add_worksheet()
  82.     ws.write(0,0,'总费用')
  83.     ws.write(0,1,total_cost)
  84.     ws.write(1,0,'车辆')
  85.     ws.write(1,1,'路径')
  86.     ws.write(1,2,'距离')
  87.     for row,route in enumerate(route_list):
  88.         route_str = [str(i) for i in route]
  89.         dist = sum(C[route[i], route[i + 1]] for i in range(len(route) - 1))
  90.         ws.write(row + 2, 0, f'{row + 1}')
  91.         ws.write(row+2,1,'-'.join(route_str))
  92.         ws.write(row + 2, 2, dist)
  93.         row += 1
  94.     wb.close()
  95. # 建模和求解
  96. def solve_model(N,I,J,K,Q,V_CAP,C,XY):
  97.     """
  98.     :param N: 所有节点
  99.     :param I: 客户节点
  100.     :param J: 车场节点
  101.     :param K: 车辆节点
  102.     :param Q: 客户需求
  103.     :param V_CAP: 车辆容量
  104.     :param C: 成本矩阵
  105.     :param XY: 节点坐标
  106.     :return: nan
  107.     """
  108.     model = Model('MDVRP')
  109.     # 添加变量
  110.     X = model.addVars(N,N,K,vtype=GRB.BINARY,name='X[i,j,k]')
  111.     U = model.addVars(K, N, vtype=GRB.CONTINUOUS, name='U[k,i]')
  112.     # 目标函数
  113.     obj = quicksum(X[i,j,k]*C[i,j] for i in N for j in N for k in K)
  114.     model.setObjective(obj,GRB.MINIMIZE)
  115.     # 需求覆盖约束
  116.     model.addConstrs( (quicksum(X[i,j,k] for j in N for k in K if i != j) == 1 for i in I),name='eq1' )
  117.     # 车辆容量约束
  118.     model.addConstrs( (quicksum(X[i,j,k]*Q[i] for i in I for j in N if i != j) <= V_CAP for k in K),name= 'eq2')
  119.     # 车辆起点约束
  120.     model.addConstrs( (quicksum(X[j,i,k] for j in J for i in I if i != j) == 1 for k in K),name='eq3' )
  121.     # 中间节点流平衡约束
  122.     model.addConstrs( (quicksum(X[i, j, k] for j in N if i != j) == quicksum(X[j, i, k] for j in N if i != j) for i in I for k in K),name='eq4' )
  123.     # 车辆终点约束
  124.     model.addConstrs( (quicksum(X[i,j,k] for i in I for j in J if i != j) == 1 for k in K), name='eq5' ) # 开放式
  125.     # model.addConstrs( (quicksum(X[j,i,k] for i in I) == quicksum(X[i,j,k] for i in I) for k in K for j in J), name='eq5')  # 不开放式
  126.     # 破除子环
  127.     model.addConstrs(U[k, i] - U[k, j] + V_CAP * X[i, j, k] <= V_CAP - Q[i] for i in I for j in I for k in K)
  128.     model.addConstrs(Q[i] <= U[k, i] for k in K for i in I)
  129.     model.addConstrs(U[k, i] <= V_CAP for k in K for i in I)
  130.     # 避免车辆直接在车场间移动
  131.     model.addConstrs( X[i,j,k] == 0 for i in J for j in J for k in K )
  132.     # 求解
  133.     model.Params.TimeLimit = 300  # 设置求解时间上限
  134.     model.optimize()
  135.     if model.status == GRB.Status.OPTIMAL or model.status == GRB.Status.TIME_LIMIT:
  136.         route_list = extract_routes(I,J,X,K)
  137.         draw_routes(route_list, XY, I,J)
  138.         save_file(route_list, model.objVal, C)
  139.     else:
  140.         model.computeIIS()
  141.         model.write('model.ilp')
  142.         for c in model.getConstrs():
  143.             if c.IISConstr:
  144.                 print(f'{c.constrName}')
  145.         print("no solution")
  146. if __name__ == '__main__':
  147.     demand_file = r'./input/demand2.csv'
  148.     depot_file = r'./input/depot.csv'
  149.     N, I, J, C, Q, XY = read_csv_file(demand_file=demand_file, depot_file=depot_file)
  150.     K = list(range(0,10))
  151.     V_CAP = 80
  152.     solve_model(N, I, J, K, Q, V_CAP, C,XY)
复制代码
4. 求解结果

4.1 开放式车场


4.2 非开放式车场


参考


  • Ramos, T. R. P., Gomes, M. I., & Póvoa, A. P. B. (2019). Multi-depot vehicle routing problem: a comparative study of alternative formulations. International Journal of Logistics Research and Applications, 23(2), 103–120. https://doi.org/10.1080/13675567.2019.1630374

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

科技颠覆者

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表