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

今天的主题是分享Python调用运筹优化求解器(Gurobi)求解VRP扩展问题之MDVRP问题的教程。

1. 模型

1.1 MDVRP问题介绍

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

1.2 数学模型

在这里插入图片描述

2. 数据结构

(1)demand文件结构
在这里插入图片描述
(2)depot文件结构

在这里插入图片描述

3. Gurobi源码

import math
import csv
import copy
import xlsxwriter
import matplotlib.pyplot as plt
from gurobipy import quicksum,Model,GRB

# 读取文件
def read_csv_file(demand_file,depot_file):
    """
    :param demand_file: 需求文件
    :param depot_file: 车场文件
    :return:
    """
    I = []
    J = []
    Q = {}
    C = {}
    XY = {}
    with open(demand_file, 'r') as f:
        demand_reader = csv.DictReader(f)
        for row in demand_reader:
            I.append(row['id'])
            Q[row['id']] = float(row['demand'])
            XY[row['id']] = (float(row['x_coord']), float(row['y_coord']))
    with open(depot_file, 'r') as f:
        depot_reader = csv.DictReader(f)
        for row in depot_reader:
            J.append(row['id'])
            XY[row['id']] = (float(row['x_coord']), float(row['y_coord']))
    N = I + J
    for i in N:
        x1, y1 = XY[i][0], XY[i][1]
        for j in N:
            x2, y2 = XY[j][0], XY[j][1]
            C[i, j] = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
    return N,I,J,C,Q,XY
# 提取路径
def extract_routes(I,J,X,K):
    I = copy.deepcopy(I)
    route_list = []
    for k in K:
        # 提取 派送阶段路径
        cur_node = None
        for j in J:
            for i in I:
                if X[j, i,k].x > 0:
                    cur_node = i
                    route = [j,i]
                    I.remove(i)
                    break
        if cur_node is None:
            continue
        while cur_node not in J:
            for i in I+J:
                if X[cur_node, i, k].x > 0:
                    cur_node = i
                    route.append(i)
                    if i not in J:
                        I.remove(i)
                    break
        route_list.append(route)
    return route_list
def draw_routes(route_list,XY,I,J):
    for route in route_list:
        path_x = []
        path_y = []
        for n in route:
            path_x.append(XY[n][0])
            path_y.append(XY[n][1])
        plt.plot(path_x, path_y, ms=5)
    demand_point_x = [XY[n][0] for n in I]
    demand_point_y = [XY[n][1] for n in I]
    depot_point_x = [XY[n][0] for n in J]
    depot_point_y = [XY[n][1] for n in J]
    plt.scatter( demand_point_x, demand_point_y, marker='s', c='b', s=30,zorder=0)
    plt.scatter( depot_point_x, depot_point_y, marker='*', c='r', s=100,zorder=1)
    plt.show()
# 保存结果
def save_file(route_list,total_cost,C):
    wb = xlsxwriter.Workbook('路径方案.xlsx')
    ws = wb.add_worksheet()
    ws.write(0,0,'总费用')
    ws.write(0,1,total_cost)
    ws.write(1,0,'车辆')
    ws.write(1,1,'路径')
    ws.write(1,2,'距离')
    for row,route in enumerate(route_list):
        route_str = [str(i) for i in route]
        dist = sum(C[route[i], route[i + 1]] for i in range(len(route) - 1))
        ws.write(row + 2, 0, f'{row + 1}')
        ws.write(row+2,1,'-'.join(route_str))
        ws.write(row + 2, 2, dist)
        row += 1
    wb.close()
# 建模和求解
def solve_model(N,I,J,K,Q,V_CAP,C,XY):
    """
    :param N: 所有节点
    :param I: 客户节点
    :param J: 车场节点
    :param K: 车辆节点
    :param Q: 客户需求
    :param V_CAP: 车辆容量
    :param C: 成本矩阵
    :param XY: 节点坐标
    :return: nan
    """
    model = Model('MDVRP')
    # 添加变量
    X = model.addVars(N,N,K,vtype=GRB.BINARY,name='X[i,j,k]')
    U = model.addVars(K, N, vtype=GRB.CONTINUOUS, name='U[k,i]')
    # 目标函数
    obj = quicksum(X[i,j,k]*C[i,j] for i in N for j in N for k in K)
    model.setObjective(obj,GRB.MINIMIZE)
    # 需求覆盖约束
    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' )
    # 车辆容量约束
    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')
    # 车辆起点约束
    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' )
    # 中间节点流平衡约束
    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' )
    # 车辆终点约束
    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' ) # 开放式
    # 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')  # 不开放式
    # 破除子环
    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)
    model.addConstrs(Q[i] <= U[k, i] for k in K for i in I)
    model.addConstrs(U[k, i] <= V_CAP for k in K for i in I)
    # 避免车辆直接在车场间移动
    model.addConstrs( X[i,j,k] == 0 for i in J for j in J for k in K )
    # 求解
    model.Params.TimeLimit = 300  # 设置求解时间上限
    model.optimize()
    if model.status == GRB.Status.OPTIMAL or model.status == GRB.Status.TIME_LIMIT:
        route_list = extract_routes(I,J,X,K)
        draw_routes(route_list, XY, I,J)
        save_file(route_list, model.objVal, C)
    else:
        model.computeIIS()
        model.write('model.ilp')
        for c in model.getConstrs():
            if c.IISConstr:
                print(f'{c.constrName}')
        print("no solution")
if __name__ == '__main__':
    demand_file = r'./input/demand2.csv'
    depot_file = r'./input/depot.csv'
    N, I, J, C, Q, XY = read_csv_file(demand_file=demand_file, depot_file=depot_file)
    K = list(range(0,10))
    V_CAP = 80
    solve_model(N, I, J, K, Q, V_CAP, C,XY)

4. 求解结果

4.1 开放式车场

在这里插入图片描述

4.2 非开放式车场

在这里插入图片描述

参考

  1. 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
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Gurobi 是一种商业化的数学优化软件,它提供了很多优化求解的功能,包括线性规划、整数规划、混合整数规划等。对于多车辆路径规划(MDVRP)问题,可以使用Gurobi进行求解。 要使用Gurobi求解MDVRP问题,需要将其转化为一个数学优化模型。可以使用线性规划或混合整数规划等方法进行建模。其中,线性规划适用于不含约束条件的MDVRP问题,而混合整数规划适用于含有约束条件的MDVRP问题。 具体地,可以使用GurobiPython接口进行建模求解。首先,需要定义变量、目标函数和约束条件。然后,将其传递给Gurobi进行求解。最后,可以获取最优解和最优值。 以下是一个简单的MDVRP问题的Gurobi建模示例: ```python import gurobipy as gp # Define the model model = gp.Model("mdvrp") # Define variables x = model.addVars(customers, vehicles, vtype=GRB.BINARY, name="x") # Define objective function obj = quicksum(distance[i,j]*x[i,j,k] for i in range(customers) for j in range(customers) for k in range(vehicles)) model.setObjective(obj, GRB.MINIMIZE) # Define constraints model.addConstrs(quicksum(x[i,j,k] for j in range(customers)) == 1 for i in range(customers) for k in range(vehicles)) model.addConstrs(quicksum(x[i,j,k] for i in range(customers)) == 1 for j in range(customers) for k in range(vehicles)) model.addConstrs(quicksum(demand[i]*x[i,j,k] for i in range(customers)) <= capacity[k] for j in range(customers) for k in range(vehicles)) model.addConstrs(quicksum(x[i,j,k] for k in range(vehicles)) <= 1 for i in range(customers) for j in range(customers)) # Solve the model model.optimize() # Print the solution for k in range(vehicles): print("Vehicle", k, ":", end=" ") for i in range(customers): for j in range(customers): if x[i,j,k].x > 0.5: print(i, "->", j, end=" ") print() ``` 其中,`customers`表示客户数量,`vehicles`表示车辆数量,`distance`表示客户之间的距离,`demand`表示客户的需求量,`capacity`表示车辆的容量限制。`x`表示二元变量,表示第k辆车是否从i点到达j点。 需要注意的是,在实际应用中,MDVRP问题通常比上述示例更为复杂,需要根据具体情况进行建模求解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Better.C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值