投稿问答最小化  关闭

万维书刊APP下载

Python | 聚焦MDVRPTW系列求解算法

2022/5/17 11:21:55  阅读:907 发布者:

摘要

     七大优化算法源码投递【蚁群算法、自适应大邻域算法、差分进化算法、粒子群算法、遗传算法、模拟退火算法、禁忌搜索算法】

致力于打造交通领域优质代码分享平台

祝大家五一快乐,外出遵守防疫政策,做好个人防护。

今天为大家分享MDVRPTW系列求解算法源码。欢迎:点赞+收藏+关注。后续将推送更多优质内容,为避免错过一个亿,建议星标。

前两期应用元启发式算法求解CVRPMDCVRP时采用了简单的路径分割规则,其优点是简单易实现,但求解质量一般。若在此基础上进一步考虑时间窗约束,将进一步降低寻优效果。为了能够在时间窗约束下寻求更好的求解效果,今天在CVRPMDCVRP代码基础上复现文献中的算法对Split函数进行改进,使其能够处理多(单)车场条件下带有时间窗约束的车辆路径规划问题。

01

注意事项

      · 求解MDVRPTWVRPTW

· 车辆类型单一

· 车辆容量不小于需求节点最大需求

· (多)车辆基地

· 各车场车辆总数满足实际需求

· 节点时间窗至少满足车辆路径只包含一个需求节点的情况

· 车场车辆总数满足实际需求

· 服务必须在要求时间窗内开始和结束

02

实现思路

Christian Prins[2]给出的CVRPSplit方法过程如下:

在此基础上Lin Chengs[3]给出了SplitVRPTW问题上的改进:

我们将在以上算法流程上略加改进,使其具备处理MDVRPTWVRPTW的能力。

03

数据结构

继续以类的形式对算法参数、编码、解码、目标函数等进行封装。相比MDCVRP系列内容,有以下调整:

 · Node数据结构类中增加‘start_time’属性表示开始服务时间,‘end_time’属性表示结束服务时间,‘service_time’表示服务时长;

· Sol数据结构中增加‘timetable_list’属性记录车辆到达每个节点的时刻,‘cost_of_distance’属性表示路径的距离成本,‘cost_of_time’表示路径的时间成本

· Model数据结构类中增加‘time_matrix’属性记录网络弧旅行时间

· splitRoutes函数参考经典方法实现

       基本数据结构定义如下:

      · Sol()类,表示一个可行解

属性     描述

obj 优化目标值

node_id_list       需求节点id有序排列集合

cost_of_distance       距离成本

cost_of_time      时间成本

route_list     车辆路径集合,对应MDVRPTW的解

timetable_list      车辆节点访问时间集合,对应MDVRPTW的解

       · Node()类,表示一个网络节点

属性     描述

id   物理节点id,需唯一

x_coord       物理节点x坐标

y_coord       物理节点y坐标

demand       物理节点需求

depot_capacity

车辆基地车队规模

start_time    最早开始服务(被服务)时间

end_time     最晚结束服务(被服务)时间

service_time       需求节点服务时间

        蚁群算法(ACO)存储算法参数部分的数据结构定义如下:

属性     描述

best_sol       全局最优解,值类型为Sol()

sol_list  蚁群集合,值类型为Sol()

demand_dict      需求节点集合(字典),值类型为Node()

depot_dict   车场节点集合(字典),值类型为Node()

depot_id_list      车场节点id集合

demand_id_list  需求节点id集合

opt_type      优化目标类型,0:最小旅行距离,1:最小时间成本

vehicle_cap 车辆容量

vehicle_speed     车辆行驶速度,用于计算旅行时间

distance_matrix  网络弧距离

time_matrix 节点旅行时间矩阵

popsize 蚁群规模

alpha     信息启发式因子

beta      期望启发式因子

Q   信息素总量

rho 信息素挥发系数

tau 网络弧信息素

tau0      路径初始信息素

       自适应大邻域搜索算法(ALNS)存储算法参数部分的数据结构定义如下(仅显示特有的数据结构定义部分):

属性     描述

rand_d_max

随机破坏程度上限

rand_d_min

随机破坏程度下限

worst_d_max

最坏破坏程度上限

worst_d_min

最坏破坏程度下限

regret_n

次优位置个数

r1

算子奖励1

r2

算子奖励2

r3

算子奖励3

rho

算子权重衰减系数

d_weight

破坏算子权重

d_select

破坏算子被选中次数/每轮

d_score

破坏算子被奖励得分/每轮

d_history_select

破坏算子历史共计被选中次数

d_history_score

破坏算子历史共计被奖励得分

r_weight

修复算子权重

r_select

修复算子被选中次数/每轮

r_score

修复算子被奖励得分/每轮

r_history_select

修复算子历史共计被选中次数

r_history_score

修复算子历史共计被奖励得分

       差分进化算法(DE)存储算法参数部分的数据结构定义如下(仅显示特有的数据结构定义部分):

属性     描述

Cr

交叉概率

F

缩放因子

popsize

种群规模

       离散粒子群算法(DPSO)存储算法参数部分的数据结构定义如下(仅显示特有的数据结构定义部分):

属性     描述

pl

可行解历史最优位置

pg

全局最优解历史最优位置

v

可行解更新速度

Vmax

最大速度

w

惯性权重

c1

学习因子

c2

学习因子

       遗传算法(GA)存储算法参数部分的数据结构定义如下(仅显示特有的数据结构定义部分):

属性     描述

pc

交叉概率

pm

突变概率

n_select

优良个体选择数量

popsize

种群规模

       模拟退火算法(SA)存储算法参数部分的数据结构定义均在前文出现过,不再赘述。

       禁忌搜索算法(TS)存储算法参数部分的数据结构定义如下(仅显示特有的数据结构定义部分):

属性     描述

tabu_list

禁忌表

TL

算子禁忌长度

04

主程序

蚁群算法(ACO)主程序如下:

def run(demand_file,depot_file,Q,tau0,alpha,beta,rho,epochs,v_cap,opt_type,popsize):

    """

    :param demand_file: demand file path

    :param depot_file: depot file path

    :param Q:信息素总量

    :param tau0: 路径信息素初始值

    :param alpha:信息启发式因子

    :param beta:期望启发式因子

    :param rho:信息挥发因子

    :param epochs:迭代次数

    :param v_cap:车辆容量

    :param opt_type:优化类型:0:最小化车辆数,1:最小化行驶距离

    :param popsize:蚁群规模

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.opt_type=opt_type

    model.alpha=alpha

    model.beta=beta

    model.Q=Q

    model.tau0=tau0

    model.rho=rho

    model.popsize=popsize

    sol=Sol()

    sol.obj=float('inf')

    model.best_sol=sol

    history_best_obj = []

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    for ep in range(epochs):

        movePosition(model)

        upateTau(model)

        history_best_obj.append(model.best_sol.obj)

        print("%s/%s best obj: %s" % (ep,epochs, model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__=='__main__':

    demand_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file,depot_file,Q=10,tau0=10,alpha=1,beta=5,rho=0.1,epochs=100,v_cap=80,opt_type=0,popsize=100)

自适应大邻域算法(ALNS)主程序如下:

def run(demand_file,depot_file,rand_d_max,rand_d_min,worst_d_min,worst_d_max,regret_n,r1,r2,r3,rho,phi,epochs,pu,v_cap,

        v_speed,opt_type):

    """

    :param demand_file: demand file path

    :param depot_file: depot file path

    :param rand_d_max: max degree of random destruction

    :param rand_d_min: min degree of random destruction

    :param worst_d_max: max degree of worst destruction

    :param worst_d_min: min degree of worst destruction

    :param regret_n:  n next cheapest insertions

    :param r1: score if the new solution is the best one found so far.

    :param r2: score if the new solution improves the current solution.

    :param r3: score if the new solution does not improve the current solution, but is accepted.

    :param rho: reaction factor of action weight

    :param phi: the reduction factor of threshold

    :param epochs: Iterations

    :param pu: the frequency of weight adjustment

    :param v_cap: Vehicle capacity

    :param v_speed Vehicle free speed

    :param opt_type: Optimization type:0:Minimize the number of vehicles,1:Minimize travel distance

    :return:

    """

    model=Model()

    model.rand_d_max=rand_d_max

    model.rand_d_min=rand_d_min

    model.worst_d_min=worst_d_min

    model.worst_d_max=worst_d_max

    model.regret_n=regret_n

    model.r1=r1

    model.r2=r2

    model.r3=r3

    model.rho=rho

    model.vehicle_cap=v_cap

    model.opt_type=opt_type

    model.vehicle_speed=v_speed

    readCSVFile(demand_file,depot_file, model)

    calDistanceTimeMatrix(model)

    history_best_obj = []

    sol = Sol()

    sol.node_id_list = genInitialSol(model.demand_id_list)

    calObj(sol, model)

    model.best_sol = copy.deepcopy(sol)

    history_best_obj.append(sol.obj)

    for ep in range(epochs):

        T=sol.obj*0.2

        resetScore(model)

        for k in range(pu):

            destory_id,repair_id=selectDestoryRepair(model)

            model.d_select[destory_id]+=1

            model.r_select[repair_id]+=1

            reomve_list=doDestory(destory_id,model,sol)

            new_sol=doRepair(repair_id,reomve_list,model,sol)

            if new_sol.obj

                sol=copy.deepcopy(new_sol)

                if new_sol.obj

                    model.best_sol=copy.deepcopy(new_sol)

                    model.d_score[destory_id]+=model.r1

                    model.r_score[repair_id]+=model.r1

                else:

                    model.d_score[destory_id]+=model.r2

                    model.r_score[repair_id]+=model.r2

            elif new_sol.obj-sol.obj

                sol=copy.deepcopy(new_sol)

                model.d_score[destory_id] += model.r3

                model.r_score[repair_id] += model.r3

            T=T*phi

            print("%s/%s:%s/%s best obj: %s" % (ep,epochs,k,pu, model.best_sol.obj))

            history_best_obj.append(model.best_sol.obj)

        updateWeight(model)

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

    print("random destory weight is {:.3f}\tselect is {}\tscore is {:.3f}".format(model.d_weight[0],

                                                                        model.d_history_select[0],

                                                                        model.d_history_score[0]))

    print("worse destory weight is {:.3f}\tselect is {}\tscore is {:.3f} ".format(model.d_weight[1],

                                                                        model.d_history_select[1],

                                                                        model.d_history_score[1]))

    print("random repair weight is {:.3f}\tselect is {}\tscore is {:.3f}".format(model.r_weight[0],

                                                                       model.r_history_select[0],

                                                                       model.r_history_score[0]))

    print("greedy repair weight is {:.3f}\tselect is {}\tscore is {:.3f}".format(model.r_weight[1],

                                                                       model.r_history_select[1],

                                                                       model.r_history_score[1]))

    print("regret repair weight is {:.3f}\tselect is {}\tscore is {:.3f}".format(model.r_weight[2],

                                                                       model.r_history_select[2],

                                                                   model.r_history_score[2]))

if __name__=='__main__':

    demand_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file=demand_file,depot_file=depot_file,rand_d_max=0.4,rand_d_min=0.1,

        worst_d_min=5,worst_d_max=20,regret_n=5,r1=30,r2=20,r3=10,rho=0.4,

        phi=0.9,epochs=5,pu=5,v_cap=80,v_speed=1,opt_type=0)

差分进化算法(DE)主程序如下:

def run(demand_file,depot_file,epochs,Cr,F,popsize,v_cap,opt_type):

    """

    :param demand_file: 需求节点文件路径

    :param depot_file: 车场节点文件路径

    :param epochs:迭代次数

    :param Cr:差分交叉概率

    :param F:缩放因子

    :param popsize:种群规模

    :param v_cap:车辆容量

    :param opt_type:优化类型:

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.Cr=Cr

    model.F=F

    model.popsize=popsize

    model.opt_type=opt_type

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    best_sol = Sol()

    best_sol.obj = float('inf')

    model.best_sol = best_sol

    genInitialSol(model)

    history_best_obj = []

    for ep in range(epochs):

        for i in range(popsize):

            v1=random.randint(0,len(model.demand_id_list)-1)

            sol=model.sol_list[v1]

            mu_x=muSol(model,v1)

            u=crossSol(model,sol.node_id_list,mu_x)

            new_sol=Sol()

            new_sol.node_id_list=u

            calObj(new_sol,model)

            if new_sol.obj<=sol.obj:

                sol=copy.deepcopy(new_sol)

                if sol.obj

                    model.best_sol=copy.deepcopy(sol)

            history_best_obj.append(model.best_sol.obj)

        print("%s/%s best obj: %s" % (ep, epochs, model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__ == '__main__':

    demand_file=r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file=r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file,depot_file, epochs=100, Cr=0.6,F=0.5, popsize=100, v_cap=80, opt_type=0)

粒子群算法(DPSO)主程序如下:

def run(demand_file,depot_file,epochs,popsize,Vmax,v_cap,v_speed,opt_type,w,c1,c2):

    """

    :param demand_file: demand file path

    :param depot_file: depot file path

    :param epochs: 迭代次数

    :param popsize: 邻域规模

    :param v_cap: 车辆容量

    :param Vmax :最大速度

    :param opt_type: 优化类型:0:最小化车辆数,1:最小化行驶距离

    :param w: 惯性权重

    :param c1:学习因子

    :param c2:学习因子

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.Vmax=Vmax

    model.vehicle_speed=v_speed

    model.opt_type=opt_type

    model.w=w

    model.c1=c1

    model.c2=c2

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    history_best_obj=[]

    genInitialSol(model,popsize)

    history_best_obj.append(model.best_sol.obj)

    for ep in range(epochs):

        updatePosition(model)

        history_best_obj.append(model.best_sol.obj)

        print("%s/%s: best obj: %s"%(ep,epochs,model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__ == '__main__':

    demand_file=r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file=r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file=demand_file,depot_file=depot_file,epochs=100,popsize=100,Vmax=2,

        v_cap=80,v_speed=1,opt_type=0,w=0.9,c1=1,c2=5)

遗传算法主程序(GA)主程序如下:

def run(demand_file,depot_file,epochs,pc,pm,popsize,n_select,v_cap,v_speed,opt_type):

    """

    :param demand_file: demand file path

    :param depot_file: depot file path

    :param epochs: Iterations

    :param pc: Crossover probability

    :param pm: Mutation probability

    :param popsize: Population size

    :param n_select: Number of excellent individuals selected

    :param v_cap: Vehicle capacity

    :param v_speed: Vehicle free speed

    :param opt_type: Optimization type:0:Minimize the cost of travel distance;1:Minimize the cost of travel time

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.pc=pc

    model.pm=pm

    model.popsize=popsize

    model.n_select=n_select

    model.opt_type=opt_type

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    generateInitialSol(model)

    history_best_obj = []

    best_sol=Sol()

    best_sol.obj=float('inf')

    model.best_sol=best_sol

    for ep in range(epochs):

        calFitness(model)

        selectSol(model)

        crossSol(model)

        muSol(model)

        history_best_obj.append(model.best_sol.obj)

        print("%s/%s best obj: %s" % (ep,epochs,model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__=='__main__':

    demand_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file=demand_file,depot_file=depot_file,epochs=100,pc=0.8,pm=0.1,popsize=100,

        n_select=80,v_cap=80,v_speed=1,opt_type=0)

模拟退火算法(SA)主程序如下:

def run(demand_file,depot_file,T0,Tf,deltaT,v_cap,opt_type):

    """

    :param demand_file: 需求节点数据文件

    :param depot_file: 车场节点数据文件

    :param T0: 初始温度

    :param Tf: 终止温度

    :param deltaT: 温度下降步长或下降比例

    :param v_cap: 车辆容量

    :param opt_type: 优化类型:0:最小化车辆数,1:最小化行驶距离

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.opt_type=opt_type

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    action_list=createActions(len(model.demand_id_list))

    history_best_obj=[]

    sol=Sol()

    sol.node_id_list=genInitialSol(model.demand_id_list)

    calObj(sol,model)

    model.best_sol=copy.deepcopy(sol)

    history_best_obj.append(sol.obj)

    Tk=T0

    nTk=len(action_list)

    while Tk>=Tf:

        for i in range(nTk):

            new_sol = Sol()

            new_sol.node_id_list = doAction(sol.node_id_list, action_list[i])

            calObj(new_sol, model)

            detla_f=new_sol.obj-sol.obj

            if detla_f<0 or math.exp(-detla_f/Tk)>random.random():

                sol=copy.deepcopy(new_sol)

            if sol.obj

                model.best_sol=copy.deepcopy(sol)

        if deltaT<1:

            Tk=Tk*deltaT

        else:

            Tk = Tk - deltaT

        history_best_obj.append(model.best_sol.obj)

        print("当前温度:%slocal obj:%s best obj: %s" % (Tk,sol.obj,model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__=='__main__':

    demand_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file=demand_file,depot_file=depot_file,T0=6000,Tf=0.001,deltaT=0.9,v_cap=80,opt_type=0)

禁忌搜索算法(TS)主程序如下:

def run(demand_file,depot_file,epochs,v_cap,opt_type):

    """

    :param demand_file: demand file path

    :param depot_file: depot file path

    :param epochs: Iterations

    :param v_cap: Vehicle capacity

    :param opt_type: Optimization type:0:Minimize the number of vehicles,1:Minimize travel distance

    :return:

    """

    model=Model()

    model.vehicle_cap=v_cap

    model.opt_type=opt_type

    readCSVFile(demand_file,depot_file,model)

    calDistanceTimeMatrix(model)

    action_list=createActions(len(model.demand_id_list))

    model.tabu_list=np.zeros(len(action_list))

    history_best_obj=[]

    sol=Sol()

    sol.node_id_list=generateInitialSol(model.demand_id_list)

    calObj(sol,model)

    model.best_sol=copy.deepcopy(sol)

    history_best_obj.append(sol.obj)

    for ep in range(epochs):

        local_new_sol=Sol()

        local_new_sol.obj=float('inf')

        for i in range(len(action_list)):

            if model.tabu_list[i]==0:

                new_sol=Sol()

                new_sol.node_id_list=doAction(sol.node_id_list,action_list[i])

                calObj(new_sol,model)

                new_sol.action_id=i

                if new_sol.obj

                    local_new_sol=copy.deepcopy(new_sol)

        sol=local_new_sol

        for i in range(len(action_list)):

            if i==sol.action_id:

                model.tabu_list[sol.action_id]=model.TL

            else:

                model.tabu_list[i]=max(model.tabu_list[i]-1,0)

        if sol.obj

            model.best_sol=copy.deepcopy(sol)

        history_best_obj.append(model.best_sol.obj)

        print("%s/%s: best obj: %s"%(ep,epochs,model.best_sol.obj))

    plotObj(history_best_obj)

    plotRoutes(model)

    outPut(model)

if __name__=='__main__':

    demand_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\demand.csv'

    depot_file = r'D:\code\py_projects\2021-05-24-algorithms-demos\datasets\MDVRPTW\depot.csv'

    run(demand_file=demand_file,depot_file=depot_file,epochs=100,opt_type=0,v_cap=80)

05

结果对比

这里采用solomon开源数据对算法进行测试,数据文件结构如下图所示,共100个需求点,3个车场,以.csv文件储存网络数据。

demand.csv文件内容如下图,其中第一行为标题栏,节点id0开始递增编号。

depot.csv文件内容如下图,其中第一行为标题栏,节点id建议采用图中命名方式,需要注意的是’ capacity’字段指的是车场配备的车辆数量。

七大算法算法收敛图与优化路线图分别如下:

06

源码获取

   本文的算法程序和数据集可通过以下百度网盘链接获取:

   链接:https://pan.baidu.com/s/1DyK77M-qeeDIZn3fUvD1Fw(或点击【阅读原文】)

   提取码:xafr

08

参考

[1].汪定伟. 智能优化方法[M]. 高等教育出版社, 2007.

[2]Prins C. A simple and effective evolutionary algorithm for the vehicle routing problem[J]. Computers & Operations Research, 2004, 31(12): 1985-2002.

[3]. Cheng L . A genetic algorithmfor the vehicle routing problem with time windows[J]. university of northcarolina wilmington, 2009.

转自:交通科研Lab 2022-05-12 18:00

文章来源于Python助力交通 ,作者Python助力交通


  • 万维QQ投稿交流群    招募志愿者

    版权所有 Copyright@2009-2015豫ICP证合字09037080号

     纯自助论文投稿平台    E-mail:eshukan@163.com