多车场路径优化丨遗传算法求解MDCVRP问题

图

路径优化系列文章:

引言

问题描述

车场车辆路径问题 (MDVRP)是基本车辆路径问题(VRP)的扩展,指的是有数个车场同时对多个用户进行服务,各用户有一定的货物需求,每个车场都可提供货物,并有车队负责执行运输任务,要求对各车场的车辆和行驶路线进行适当的安排,在保证满足各用户的需求的前提下,使总的运输成本最低。

问题满足假设:

  • 1、各车场派出车辆的数目不能超过该车场所拥有的车辆数;

  • 2、各车辆都是从各自的车场出发,并回到原车场;

  • 3、每个用户只能被一辆车服务一次;4、车辆满足载重约束。

数学模型

图1

数学模型:

图2

算法数据处理

数据:

本文是数据是3个车场和100个需求量,各个车场的车辆数是20,具体车场和需求点的坐标及需求点的需求量见两个depot和demand文件,车辆载重暂时设为50。模型是一般的路径优化模型,目标函数设置为3个车场派出的所有车辆的路径总长度。

数据.png
在这里插入图片描述

以3个车场为原点,分别和100个需求点组合,计算组合后的任意两点的距离,形成3个101×101的距离矩阵。距离矩阵计算比较简单,计算矩阵右上角的值即可,左下角的值和右上角的值对称。

距离矩阵

def distance(dep_demand):                 # 3个点和需求点距离矩阵计算
    dis = np.zeros((len(dep_demand),len(dep_demand)))
    for i in range(len(dep_demand)-1):
        for j in range(i+1):
            point1 = dep_demand[i]
            point2 = dep_demand[j]
            di = np.sqrt((point1[0]-point2[0])**2+(point1[1]-point2[1])**2)
            dis[i,j], dis[j,i] = di, di   # 距离关于对角线对称
    return dis

编码及解码

编码

采用实数编码的方式,用1到100共100个数表示100个需求点,随机打乱即是一个可行的编码:

road = random.sample(range(1,101),100)

一个可行编码:

road = [95, 34, 71, 23, 18, 17, 80, 91, 96, 60, 50, 28, 72, 37, 30, 41, 33, 2, 15, 66, 36, 73, 38, 82, 49, 84, 31, 16, 3, 7, 43, 6, 4, 20, 13, 5, 93, 52, 70, 53, 29, 89, 67, 98, 26, 45, 100, 79, 87, 64, 59, 81, 44, 61, 63, 83, 8, 76, 92, 12, 32, 1, 9, 77, 75, 46, 65, 25, 85, 24, 69, 86, 58, 19, 22, 74, 88, 90, 94, 21, 55, 39, 42, 51, 27, 10, 48, 35, 97, 57, 11, 56, 40, 54, 62, 99, 68, 78, 14, 47]

解码

核心是遍历到某个需求点时,寻找最优车场的最优车辆。一个车场的最优车辆寻找如下:

step1:遍历该车场的所有车辆,如果至少有一辆车的剩余载重满足该需求点,计算对应车辆的最后一个点到该需求点的距离,再加上该需求点到对应回归车场的距离,转入步骤3,如果没有车辆的剩余载重满足,转入step2

step2:新增一辆车,

计算车场到该需求点的距离,再加上需求点到车场的距离,车场的最优车辆只有一个,即最优

step3:比较各个车辆对应的计算距离,最优个体是距离最短对应的车辆

3个车场的最优个体做比较,距离最短是整个系统的最优个体。这种车辆选择方式采用一种贪婪的思想,寻找使路径最短的车辆,一定程度上会加快最优路径的寻找。

核心代码如下:

def fitness(self,road):
        p1, p2, p3 = [[]], [[]], [[]]   # 存车场到需求点的路径
        dis_p =[0, 0, 0]
        
        d1, d2, d3 = [[]], [[]], [[]]
        for w in range(len(road)):
            point = road[w]            # 新的点

            p = [p1, p2, p3]
            dis = [self.dis1, self.dis2, self.dis3]  # 3个车场到需求点的距离矩阵
            s = []
            d = [d1, d2, d3]
            location = []
            for i in range(3):
                p_now = p[i]           # 依次从第1车场到第3车场
                dis_now = dis[i]
                d_now = d[i]
                signal = 0
                if len(p_now[-1]) > 0: # 如果最后一个车的装载情况非空
                    dx = []
                    loc = []
                    for j in range(len(p_now)):     # 依次遍历对应车场的各个车
                        now_load = sum(d_now[j])    # 计算每个车的装载情况
                        if now_load + self.demand.iloc[point-1,-1] <= self.load:  # 如果可以装
                            dw = dis_now[p_now[j][-1], point] + dis_now[point,0]  # 计算其与上一个点的距离和回到车场的距离
                            dx.append(dw)
                            loc.append(j)
                    if len(dx)>0:
                        di = min(dx)             # 挑选最小距离对应的车
                        idx = dx.index(min(dx))
                        best_idx = loc[idx]
                    if len(dx)==0:               # 如果各个车都不能装,进入下一步
                        signal=1
                    if len(p_now[-1]) == 0 or signal==1: # 如果车场最后一个车是空车,或者上一步没装成功
                        
                        now_car = len(p_now)       # 现在的车场对应的车辆使用情况
                        if now_car + 1 <= self.car_number:  # 如果使用车辆加1小于等于20辆,增加新车
                            if i ==0 :
                                p1.append([])
                                d1.append([])
                            if i ==1 :
                                p2.append([])
                                d2.append([])
                            if i ==2:
                                p3.append([])
                                d3.append([])
                        else:                    # 如果使用车辆加1大于20辆,距离赋予一个很大的值,对应车不能装,也没有增加新车
                            di = 100000
                            best_idx = 111111

            
                if len(p_now[-1]) == 0:        # 如果有新车
                    di = dis_now[0, point] + dis_now[point, 0] # 计算车场到需求点及回去的距离
                    best_idx = -1
                s.append(di)
                location.append(best_idx)
            idx = s.index(min(s))              # 挑选最小距离对应的车场
            idd = location[idx]                # 对应车场对应的车辆选择  
            
            if idx ==0 :                      # 车场1对应车辆添加需求点,下面相同
                p1[idd].append(point)
                d1[idd].append(self.demand.iloc[point-1,-1])
            if idx ==1 :
                p2[idd].append(point)
                d2[idd].append(self.demand.iloc[point-1,-1])
            if idx ==2:
                p3[idd].append(point)
                d3[idd].append(self.demand.iloc[point-1,-1])
            dis_p[idx] += min(s)              # 各个车场对应的距离累计
            
        return p1, p2, p3, dis_p, d1, d2, d3

遗传算法

路径交叉

采用pox的交叉方式,1到100随机生成一个数,假设生成的数是20,对于两个可行的编码road1和road2,大于20的基因不变,小于20的基因位置填入另一个体的编码:

以a=[1,3,2,4]和b=[2,4,1,3]为例,假设生成的数是3 ,a,b中的3,4基因不变,1和2位置填入对面的1,2基因,结果a1 = [2,3,1,4],b1=[1,4,2,3]

核心代码:

def road_cross(self,road1,road2):           # pox交叉
        num = random.randint(1, len(road1)+1)
        
        un1, un2 = [],[]
        for i in range(len(road1)):
            if road1[i] < num:             # 小于num的编码
                un1.append(road1[i])  
                road1[i] = 0               # 对应位置基因设为0
            if road2[i] < num:
                un2.append(road2[i])
                road2[i] = 0
        
        for j in range(len(road1)):
            if road1[j] == 0:
                road1[j] = un2[0]          # road1对应位置填充road2小于num的基因
                del un2[0]                 # 删除填充基因
            if road2[j] == 0:
                road2[j] = un1[0]          # road2对应位置填充road1小于num的基因
                del un1[0]                 # 删除填充基因
        return road1, road2

路径变异

采用逆序变异的方式:随机生成两个位置对于两个位置的基因逆序。

如a=[1,3,2,4],生成位置1和位置3,变异后的结果是a1=[2,3,1,4]

核心代码:

def road_mul(self,road):                              # 逆序变异
        index = random.sample(range(len(road)), 2)  # 随机生成两个位置
        idx = list(set(index))                      # 位置按先后顺序调整

        d = road[idx[0]:idx[1] + 1]
        d.reverse()  # 逆序
        road[idx[0]:idx[1] + 1] = d
        return road

个体选择

采用精英策略,每次迭代选择最优的一半个体:

核心代码:

sort = np.array(answer).argsort()                     # 总长度从小到大的索引
Road1 = np.array(Road)[sort][:self.popsize].tolist()  # 取一半
answer1 = np.array(answer)[sort][:self.popsize].tolist()

结果

主函数:

from decode import decoding
from ga import GA

parm_car = [50,20]                              # 载重和车场车辆数
de = decoding(parm_car)                         # 解码模块

parm_ga = [50,100,.8,.2]                        # 迭代次数,种群规模,交叉概率,变异概率
ga = GA(parm_ga,de)
road,answer,result = ga.ga_total()              # 遗传寻优结果
p1, p2, p3, dis_p,d1, d2, d3 = de.fitness(road) # 各车场的路径等结果


de.draw(p1,p2,p3,dis_p)                         # 各车场路径
de.draw_change(result)                          # 路径总长度变化

运行结果展示:

1

路径图:

2)

路径长度变化:

3)

视频演示:

视频

多车场车辆路径优化丨遗传算法求解

参考文献

[1]邹彤,李宁,孙德宝等.多车场车辆路径问题的遗传算法[J].计算机工程与应用,2004(21):82-83.

完整算法+数据:

#关注 微信公众号:学长带你飞
# 主要更新方向:1、车间调度问题求解算法
#              2、学术写作技巧
#              3、读书感悟
# @Author  : Jack Hao
可加微信:diligent-1618,请备注:学校-专业-昵称。

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
车场车辆路径问题是指在多个车场中,如何合理地安排车辆的路径,以最小化总行驶距离或时间。遗传算法是一种启发式优化算法,可以用于解决这类问题遗传算法的基本思想是模拟生物进化过程中的遗传和适应度选择机制。它通过对候选解进行编码、交叉、变异等操作,生成新的解,并根据适应度函数对解进行评估和选择,从而逐步优化得到最优解。 在多车场车辆路径问题中,遗传算法可以按照以下步骤进行求解: 1. 初始化种群:随机生成一组初始解,每个解表示一种车辆路径方案。 2. 适应度评估:根据问题的具体要求,定义适应度函数来评估每个解的质量,例如总行驶距离或时间。 3. 选择操作:根据适应度函数的值,采用选择算子(如轮盘赌选择)选择一部分优秀的解作为父代。 4. 交叉操作:对选出的父代进行交叉操作,生成新的解。交叉可以通过交换路径片段、插入或删除节点等方式进行。 5. 变异操作:对新生成的解进行变异操作,引入一定的随机性。变异可以通过交换节点位置、改变路径顺序等方式进行。 6. 更新种群:将新生成的解与原有种群进行合并,得到更新后的种群。 7. 终止条件判断:根据预设的终止条件(如达到最大迭代次数或找到满意解),判断是否结束算法。 8. 返回最优解:根据适应度函数的值,选择最优的解作为问题的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值