【改进篇】Python实现VRP常见求解算法——遗传算法(GA)

基于python语言,实现经典遗传算法(GA)对车辆路径规划问题(CVRP)进行求解, 优化代码结构,改进Split函数

往期优质资源

1. 适用场景

  • 求解CVRP
  • 车辆类型单一
  • 车辆容量不小于需求节点最大需求
  • 单一车辆基地

2. 改进效果对比

这里做了简单的参数敏感性分析,比较不同参数组合下两个版本code的最优值与求解时间的差异。具体为:依次设定交叉概率为0.7,0.8,0.9,其他参数固定不变:最大迭代次数为500,种群规模为100,选择个体为80,变异概率0.2。具体实验结果如下表:
在这里插入图片描述

可以看出,改进后的code,目标函数优化 3% 7% 不等,但牺牲了40%左右的计算时间。这里计算时间以秒为单位,所以改进后的代码运算时间还是可以接受的。

3. 求解结果

(1)收敛曲线
在这里插入图片描述

(2)车辆路径
在这里插入图片描述

4. 改进内容

本期代码在前期代码的基础上做了以下改进:

  • 增加距离矩阵,减少代码中重复计算节点间距离代码
  • 引入文献中基于图论的Split方法,在给定节点id序列时,可求得最优分割方案

除了以上关键改动之外,还对代码做了细小调整。

5. 部分代码

(1)数据结构

# 数据结构:解
class Sol():
    def __init__(self):
        self.node_no_seq = None # 解的编码
        self.obj_val = None # 目标函数
        self.fitness = None  # 适应度
        self.route_list = None # 解的解码
# 数据结构:网络节点
class Demand():
    def __init__(self):
        self.id = 0 # 节点id
        self.x_coord = 0 # 节点平面横坐标
        self.y_coord = 0  # 节点平面纵坐标
        self.demand = 0 # 节点需求
# 数据结构:全局参数
class Model():
    def __init__(self):
        self.best_sol = None # 全局最优解
        self.demand_dict = {}  # 需求节点集合
        self.demand_id_list = []
        self.sol_list = []  # 解的集合
        self.depot = None # 车场节点
        self.number_of_nodes = 0 # 需求节点数量
        self.vehicle_cap = 80 # 车辆最大容量
        self.distance_matrix = {}
        self.pc = 0.5 # 交叉概率
        self.pm = 0.2 # 变异概率
        self.n_select = 80 # 种群选择数量
        self.popsize = 100 # 种群规模

(2)距离矩阵

def cal_distance_matrix(model):
    for i in range(len(model.demand_id_list)):
        f_n = model.demand_id_list[i]
        for j in range(i + 1, len(model.demand_id_list)):
            t_n = model.demand_id_list[j]
            dist = math.sqrt((model.demand_dict[f_n].x_coord - model.demand_dict[t_n].x_coord) ** 2
                             + (model.demand_dict[f_n].y_coord - model.demand_dict[t_n].y_coord) ** 2)
            model.distance_matrix[f_n, t_n] = dist
            model.distance_matrix[t_n, f_n] = dist

        dist = math.sqrt((model.demand_dict[f_n].x_coord - model.depot.x_coord) ** 2
                         + (model.demand_dict[f_n].y_coord - model.depot.y_coord) ** 2)
        model.distance_matrix[f_n, model.depot.id] = dist
        model.distance_matrix[model.depot.id, f_n] = dist

(3)路径提取

def extractRoutes(node_no_seq,P,depot_id):
    route_list = []
    route = []
    p = P[node_no_seq[0]]
    for node_seq in node_no_seq:
        if P[node_seq] == p:
            route.append(node_seq)
        else:
            route.insert(0,depot_id)
            route.append(depot_id)
            route_list.append(route)
            route = [node_seq]
            p = P[node_seq]
    return route_list

(4)收敛曲线

def plotObj(obj_list):
    plt.rcParams['font.sans-serif'] = ['SimHei'] #show chinese
    plt.rcParams['axes.unicode_minus'] = False  # Show minus sign
    plt.plot(np.arange(1,len(obj_list)+1),obj_list)
    plt.xlabel('Iterations')
    plt.ylabel('Obj Value')
    plt.grid()
    plt.xlim(1,len(obj_list)+1)
    plt.show()

(5)车辆路径

def plotRoutes(model):
    for route in model.best_sol.route_list:
        x_coord = [model.depot.x_coord]
        y_coord = [model.depot.y_coord]
        for node_no in route[1:-1]:
            x_coord.append(model.demand_dict[node_no].x_coord)
            y_coord.append(model.demand_dict[node_no].y_coord)
        x_coord.append(model.depot.x_coord)
        y_coord.append(model.depot.y_coord)
        plt.plot(x_coord, y_coord, marker='s', color='b', linewidth=0.5)
    plt.show()

(6)主程序

def run(filepath,epochs,pc,pm,popsize,n_select,v_cap):
    """
    :model filepath: Xlsx文件路径
    :model epochs: 迭代次数
    :model pc: 交叉概率
    :model pm: 变异概率
    :model popsize: 种群规模
    :model n_select: 优秀父代保留数量
    :model v_cap: 车辆容量
    :model opt_type: 优化目标,0:最小化车辆数,1:最小化总行驶距离
    :return:
    """
    model=Model()
    model.vehicle_cap=v_cap
    model.pc=pc
    model.pm=pm
    model.popsize=popsize
    model.n_select=n_select
    readXlsxFile(filepath,model)
    cal_distance_matrix(model)
    genInitialSol(model)
    history_best_obj = []
    best_sol=Sol()
    best_sol.obj_val=float('inf')
    model.best_sol=best_sol
    start = time.time()
    for ep in range(epochs):
        calFit(model)
        selectSol(model)
        crossSol(model)
        muSol(model)
        history_best_obj.append(model.best_sol.obj_val)
        print("%s/%s, best obj: %s time : %s" % (ep,epochs,model.best_sol.obj_val,time.time()-start))
    plotObj(history_best_obj)
    plotRoutes(model)
    outPut(model)

6. 完整代码

如有错误,欢迎交流。
有偿获取

参考

  1. A simple and effective evolutionary algorithm for the vehicle routing problem
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Better.C

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

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

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

打赏作者

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

抵扣说明:

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

余额充值