栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

【进阶四】Python实现(MD)HVRP常见求解算法——遗传算法(GA)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【进阶四】Python实现(MD)HVRP常见求解算法——遗传算法(GA)

遗传算法+Split 求解异构车辆路径规划问题

对孤峰绝顶,云烟竞秀

目录
  • 信息传递
  • 1. 适用场景
  • 2. 求解效果
  • 3. 代码分析
  • 4. 数据格式
  • 5. 分步实现
  • 6. 完整代码
  • 参考

信息传递
  • python实现6种智能算法求解CVRP问题
  • python实现7种智能算法求解MDVRP问题
  • python实现7种智能算法求解MDVRPTW问题
  • Python版MDHFVRPTW问题智能求解算法代码【TS算法】
  • Python版MDHFVRPTW问题智能求解算法代码【SA算法】
  • Python版MDHFVRPTW问题智能求解算法代码【GA算法】
  • Python版MDHFVRPTW问题智能求解算法代码【DPSO算法】
  • Python版MDHFVRPTW问题智能求解算法代码【DE算法】
  • Python版MDHFVRPTW问题智能求解算法代码【ACO算法】
  • Python版HVRP问题智能求解算法代码【GA算法】
1. 适用场景
  • 求解HVRP或MDHVRP
  • 多车辆类型
  • 车辆容量不小于需求节点最大需求
  • (多)车辆基地
  • 车场车辆总数满足实际需求
2. 求解效果

(1)收敛曲线

(2)车辆路径

3. 代码分析

Christian Prins给出的HVRP的Split方法过程如下:

以上算法在搜索过程中需求节点的备选标签规模会非常大,占用内存且降低搜索效率,因此采用了帕累托思想来遗传非支配解(19行~21行)。为了进一步控制备选标签数量,本期文章还附加实现了通过剩余容量与剩余需求来判断是否产生新的备选标签,提前避免产生不可行的标签。尽管如此,当考虑的异构车辆类型较多、需求点规模较大时,求解效率依然较低(代码结构、python特性、硬件环境)。所以本期文章进一步加入了最大标签数量参数来控制每个需求节点的备选标签规模,通过调节该参数实现速度与质量的相对均衡。

4. 数据格式

这里采用solomon开源数据对算法进行测试,数据文件结构如下图所示,共100个需求点,3个车场,3类车辆,以.csv文件储存数据。
demand.csv文件内容如下图,其中第一行为标题栏,节点id从0开始递增编号。

depot.csv文件内容如下图,其中第一行为标题栏,依次为:车场id,车场平面x坐标,车场平面y坐标,车辆类型,车辆装载量,车辆数量,固定成本,单位里程运输成本。

5. 分步实现

(1)代码整体架构

(2)数据结构
定义Sol()类,Demand()类,Vehicle()类,Model()类,其属性如下表:

  • Sol()类,表示一个可行解
属性描述
node_id_list需求节点id有序排列集合
obj优化目标值
fitness解的适应度
route_list车辆路径集合,对应MDVRPTW的解
  • Demand()类,表示一个需求节点
属性描述
id物理节点id,需唯一
x_coord物理节点x坐标
y_coord物理节点y坐标
demand物理节点需求
  • Vehicle()类,表示一个车队节点
属性描述
depot_id车辆归属的车场节点节点id,需唯一
x_coord车辆归属车场节点x坐标
y_coord车辆归属车场节点y坐标
type车辆类型
capacity车辆容量
free_speed车辆运营速度
fixed_cost车辆固定成本
variable_cost车辆变动成本
  • Model()类,存储算法参数
属性描述
best_sol全局最优解,值类型为Sol()
demand_dict需求节点集合(字典),值类型为Demand()
vehicle_dict车队集合(字典),值类型为Vehicle()
vehicle_type_list车队id集合
demand_id_list需求节点id集合
sol_list种群,值类型为Sol()
distance_matrix节点距离矩阵
number_of_demands需求节点数量
pc交叉概率
pm突变概率
n_select优良个体选择数量
popsize种群规模

(3)数据读取

def readCSVFile(demand_file,depot_file,model):
    with open(demand_file,'r') as f:
        demand_reader=csv.DictReader(f)
        for row in demand_reader:
            demand = Demand()
            demand.id = int(row['id'])
            demand.x_coord = float(row['x_coord'])
            demand.y_coord = float(row['y_coord'])
            demand.demand = float(row['demand'])
            model.demand_dict[demand.id] = demand
            model.demand_id_list.append(demand.id)
        model.number_of_demands=len(model.demand_id_list)

    with open(depot_file, 'r') as f:
        depot_reader = csv.DictReader(f)
        for row in depot_reader:
            vehicle = Vehicle()
            vehicle.depot_id = row['depot_id']
            vehicle.x_coord = float(row['x_coord'])
            vehicle.y_coord = float(row['y_coord'])
            vehicle.type = row['vehicle_type']
            vehicle.capacity=float(row['vehicle_capacity'])
            vehicle.numbers=float(row['number_of_vehicle'])
            vehicle.fixed_cost=float(row['fixed_cost'])
            vehicle.variable_cost=float(row['variable_cost'])
            model.vehicle_dict[vehicle.type] = vehicle
            model.vehicle_type_list.append(vehicle.type)

(4)距离计算

def calDistanceMatrix(model):
    for i in range(len(model.demand_id_list)):
        from_node_id = model.demand_id_list[i]
        for j in range(i + 1, len(model.demand_id_list)):
            to_node_id = model.demand_id_list[j]
            dist = math.sqrt((model.demand_dict[from_node_id].x_coord - model.demand_dict[to_node_id].x_coord) ** 2
                             + (model.demand_dict[from_node_id].y_coord - model.demand_dict[to_node_id].y_coord) ** 2)
            model.distance_matrix[from_node_id, to_node_id] = int(dist)
            model.distance_matrix[to_node_id, from_node_id] = int(dist)
        for _, vehicle in model.vehicle_dict.items():
            dist = math.sqrt((model.demand_dict[from_node_id].x_coord - vehicle.x_coord) ** 2
                             + (model.demand_dict[from_node_id].y_coord - vehicle.y_coord) ** 2)
            model.distance_matrix[from_node_id, vehicle.type] = int(dist)
            model.distance_matrix[vehicle.type, from_node_id] = int(dist)

(5)路径成本计算

def calTravelCost(route_list,model):
    obj=0
    for route in route_list:
        vehicle=model.vehicle_dict[route[0]]
        distance=0
        fixed_cost=vehicle.fixed_cost
        variable_cost=vehicle.variable_cost
        for i in range(len(route) - 1):
            from_node = route[i]
            to_node = route[i + 1]
            distance += model.distance_matrix[from_node, to_node]
        obj += fixed_cost + distance * variable_cost
    return obj

(6)适应度计算

def calFitness(model):
    max_obj=-float('inf')
    best_sol=Sol()
    best_sol.obj=float('inf')
    number_of_split_failures=0
    # calculate travel distance
    for sol in model.sol_list:
        node_id_list=copy.deepcopy(sol.node_id_list)
        ret=splitRoutes(node_id_list, model)
        if ret is not None:
            sol.route_list=ret
            sol.obj=calTravelCost(sol.route_list,model)
            if sol.obj > max_obj:
                max_obj=sol.obj
            if sol.obj < best_sol.obj:
                best_sol=copy.deepcopy(sol)
        else:
            number_of_split_failures+=1
            sol.obj=None
    # calculate fitness
    for sol in model.sol_list:
        sol.fitness=max_obj-sol.obj if sol.obj is not None else max_obj
    if best_sol.obj=model.popsize*0.3:
        print("There are {} sols that are unfeasible. Please increase the number of vehicles to obtain better solutions."
              .format(number_of_split_failures))

(7)初始解

def generateInitialSol(model):
    demand_id_list=copy.deepcopy(model.demand_id_list)
    for i in range(model.popsize):
        seed=int(random.randint(0,10))
        random.seed(seed)
        random.shuffle(demand_id_list)
        sol=Sol()
        sol.node_id_list=copy.deepcopy(demand_id_list)
        model.sol_list.append(sol)

(8)个体选择

def selectSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    for i in range(model.n_select):
        f1_index=random.randint(0,len(sol_list)-1)
        f2_index=random.randint(0,len(sol_list)-1)
        f1_fit=sol_list[f1_index].fitness
        f2_fit=sol_list[f2_index].fitness
        if f1_fit 

(9)交叉

def crossSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        f1_index = random.randint(0, len(sol_list) - 1)
        f2_index = random.randint(0, len(sol_list) - 1)
        if f1_index!=f2_index:
            f1 = copy.deepcopy(sol_list[f1_index])
            f2 = copy.deepcopy(sol_list[f2_index])
            if random.random() <= model.pc:
                cro1_index=int(random.randint(0,len(model.demand_id_list)-1))
                cro2_index=int(random.randint(cro1_index,len(model.demand_id_list)-1))
                new_c1_f = []
                new_c1_m=f1.node_id_list[cro1_index:cro2_index+1]
                new_c1_b = []
                new_c2_f = []
                new_c2_m=f2.node_id_list[cro1_index:cro2_index+1]
                new_c2_b = []
                for index in range(len(model.demand_id_list)):
                    if len(new_c1_f)model.popsize:
                break

(10)变异

def muSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        f1_index = int(random.randint(0, len(sol_list) - 1))
        f1 = copy.deepcopy(sol_list[f1_index])
        m1_index=random.randint(0,len(model.demand_id_list)-1)
        m2_index=random.randint(0,len(model.demand_id_list)-1)
        if m1_index!=m2_index:
            if random.random() <= model.pm:
                node1=f1.node_id_list[m1_index]
                f1.node_id_list[m1_index]=f1.node_id_list[m2_index]
                f1.node_id_list[m2_index]=node1
                model.sol_list.append(copy.deepcopy(f1))
            else:
                model.sol_list.append(copy.deepcopy(f1))
            if len(model.sol_list)>model.popsize:
                break

(11)绘制收敛曲线

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()

(12)绘制车辆路径

def plotRoutes(model):
    for route in model.best_sol.route_list:
        x_coord=[model.vehicle_dict[route[0]].x_coord]
        y_coord=[model.vehicle_dict[route[0]].y_coord]
        for node_id in route[1:-1]:
            x_coord.append(model.demand_dict[node_id].x_coord)
            y_coord.append(model.demand_dict[node_id].y_coord)
        x_coord.append(model.vehicle_dict[route[-1]].x_coord)
        y_coord.append(model.vehicle_dict[route[-1]].y_coord)
        plt.grid()
        if route[0]=='v1':
            plt.plot(x_coord,y_coord,marker='o',color='black',linewidth=0.5,markersize=5)
        elif route[0]=='v2':
            plt.plot(x_coord,y_coord,marker='o',color='orange',linewidth=0.5,markersize=5)
        elif route[0]=='v3':
            plt.plot(x_coord,y_coord,marker='o',color='r',linewidth=0.5,markersize=5)
        else:
            plt.plot(x_coord, y_coord, marker='o', color='b', linewidth=0.5, markersize=5)
    plt.xlabel('x_coord')
    plt.ylabel('y_coord')
    plt.show()

(13)输出结果

def outPut(model):
    work=xlsxwriter.Workbook('result.xlsx')
    worksheet=work.add_worksheet()
    worksheet.write(0, 0, 'obj')
    worksheet.write(0, 1,model.best_sol.obj)
    worksheet.write(1, 0,'vehicleID')
    worksheet.write(1, 1,'depotID')
    worksheet.write(1, 2, 'vehicleType')
    worksheet.write(1, 3,'route')
    for row,route in enumerate(model.best_sol.route_list):
        worksheet.write(row+2,0,str(row+1))
        depot_id=model.vehicle_dict[route[0]].depot_id
        worksheet.write(row+2,1,depot_id)
        worksheet.write(row+2,2,route[0])
        r=[str(i)for i in route]
        worksheet.write(row+2,3, '-'.join(r))
    work.close()

(14)主函数

def run(demand_file,depot_file,epochs,pc,pm,popsize,n_select,max_labels):
    """
    :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 max_labels: maxium number of labels of each node
    :return:
    """
    model=Model()
    model.pc=pc
    model.pm=pm
    model.popsize=popsize
    model.n_select=n_select
    model.max_labels = max_labels
    readCSVFile(demand_file,depot_file,model)
    calDistanceMatrix(model)
    generateInitialSol(model)
    history_best_obj = []
    best_sol=Sol()
    best_sol.obj=float('inf')
    model.best_sol=best_sol
    start_time=time.time()
    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: %.3f, runtime: %.3f" % (ep+1,epochs,model.best_sol.obj,time.time()-start_time))
    plotObj(history_best_obj)
    plotRoutes(model)
    outPut(model)
6. 完整代码

如有错误,欢迎交流。

https://download.csdn.net/download/python_n/85619855

参考
  1. Order-first split-second methods for vehicle routing problems: A review
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/976815.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号