VRPTW建模与求解—基于粒子群算法

2023-05-16

VRPTW建模与求解—基于粒子群算法

1 VRPTW简要描述

VRPTW(Vehicle Routing Problem with Time Windows)是指在经典VRP的前提上,给每个客户增添时间窗约束以进行运输服务,时间窗是一个顾客与厂商根据双方需求协商好的特定时间段,包含最早可到达时间和最晚必须到达时间。带时间窗约束使得 VRP 更加复杂性,通常可把时间窗分为硬时间窗(必须满足)、软时间窗(可以不满足,但会受到惩罚)。

2 课题场景设计

2.1 场景

单向:纯取货/纯送货;
单配送中心:只有一个配送中心/车场;
单车型:只考虑一种车型,
需求不可拆分:客户需求只能有一辆车满足;
车辆封闭:完成配送任务的车辆需回到配送中心;
车辆充足:不限制车辆数量,即配送车辆需求均能满足;
非满载:任意客户点的需求量小于车辆最大载重;
软时间窗:[最早可服务时间ET,最晚可服务时间LT],早于ET会产生等待成本,晚于LT会有惩罚成本;

2.2 要求

优化目标:最小化车辆启动成本、车辆行驶成本、等待成本和惩罚成本之和;
约束条件:车辆行驶距离约束,重量约束,时间窗约束;
已知信息:配送中心位置、客户点位置、客户点需求、客户服务时间窗要求,客户服务时间,车辆最大载重、车辆最大行驶距离、车辆行驶速度、车辆启动成本、车辆单位距离行驶成本,等待成本,惩罚成本;

3 数学模型

3.1 符号定义:

在这里插入图片描述

3.2 数学模型

在这里插入图片描述
在这里插入图片描述

4 粒子群算法设计

4.1 算法设计

见【CVRP建模与求解-基于粒子群算法(python实现)】,算法设计一致。唯一区别在于在计算适应度时,VRPTW需要将车辆等待成本和惩罚成本记录,这两项惩罚成本的计算方法将见数学模型约束 f.惩罚成本。

4.2 python程序设计

# -*- coding: utf-8 -*-
import math
import random
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 添加这条可以让图形显示中文


def calDistance(CityCoordinates):
    ''' 
    计算城市间距离
    输入:CityCoordinates-城市坐标;
    输出:城市间距离矩阵-dis_matrix
    '''
    dis_matrix = pd.DataFrame(data=None,columns=range(len(CityCoordinates)),index=range(len(CityCoordinates)))
    for i in range(len(CityCoordinates)):
        xi,yi = CityCoordinates[i][0],CityCoordinates[i][1]
        for j in range(len(CityCoordinates)):
            xj,yj = CityCoordinates[j][0],CityCoordinates[j][1]
            dis_matrix.iloc[i,j] = round(math.sqrt((xi-xj)**2+(yi-yj)**2),2)
    return dis_matrix


def greedy(CityCoordinates,dis_matrix):
    '''
    贪婪策略构造初始解
    输入:CityCoordinates-节点坐标,dis_matrix-距离矩阵
    输出:初始解-line
    '''
    #修改dis_matrix以适应求解需要
    dis_matrix = dis_matrix.astype('float64')
    for i in range(len(CityCoordinates)):dis_matrix.loc[i,i]=math.pow(10,10)
    dis_matrix.loc[:,0]=math.pow(10,10)#0不在编码内
    line = []#初始化
    now_city = random.randint(1,len(CityCoordinates)-1)#随机生成出发城市
    line.append(now_city)#添加当前城市到路径
    dis_matrix.loc[:,now_city] = math.pow(10,10)#更新距离矩阵,已经过城市不再被取出
    for i in range(1,len(CityCoordinates)-1):
        next_city = dis_matrix.loc[now_city,:].idxmin()#距离最近的城市
        line.append(next_city)#添加进路径
        dis_matrix.loc[:,next_city] = math.pow(10,10)#更新距离矩阵
        now_city = next_city#更新当前城市
    return line


def calFitness(birdPop,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1,C2,C3,time,V):
    '''
    贪婪策略分配车辆(解码),计算路径距离(评价函数)
    输入:birdPop-路径,Demand-客户需求,dis_matrix-城市间距离矩阵,CAPACITY-车辆最大载重,DISTABCE-车辆最大行驶距离,C0-车辆启动成本,
    C1-车辆单位距离行驶成本,C2-等待成本,C3-惩罚成本,time-服务时间窗和服务时间;
    输出:birdPop_car-分车后路径,fits-适应度
    '''
    birdPop_car,fits = [],[]#初始化
    for i in range(len(birdPop)):
        bird = birdPop[i]
        lines = []#存储线路分车
        line = [0]#每辆车服务客户点
        dis_sum = 0#线路距离
        dis,d = 0,0#当前客户距离前一个客户的距离、当前客户需求量
        i = 0#指向配送中心
        time_point = 0#
        wait = 0
        late = 0
        
        while i < len(bird):
            if line == [0]:#车辆未分配客户点
                dis += dis_matrix.loc[0,bird[i]]#记录距离
                line.append(bird[i])#为客户点分车
                d += Demand[bird[i]]#记录需求量
                time_point += dis_matrix.loc[0,bird[i]]/V
                if time_point < time[bird[i]][0]:
                    wait = time[bird[i]][0] - time_point
                    time_point = time_point + wait + time[bird[i]][2]
                elif time_point > time[bird[i]][1]:
                    late = time_point - time[bird[i]][1]
                    time_point = time_point + time[bird[i]][2]
                else:
                    time_point = time_point + time[bird[i]][2]
                
                i += 1#指向下一个客户点
            else:#已分配客户点则需判断车辆载重和行驶距离
                if (dis_matrix.loc[line[-1],bird[i]]+dis_matrix.loc[bird[i],0]+ dis <= DISTABCE) & (d + Demand[bird[i]]<=CAPACITY ) :
                    dis += dis_matrix.loc[line[-1],bird[i]]
                    time_point += dis_matrix.loc[line[-1],bird[i]]/V
                    if time_point < time[bird[i]][0]:
                        wait = time[bird[i]][0] - time_point
                        time_point = time_point + wait + time[bird[i]][2]
                    elif time_point > time[bird[i]][1]:
                        late = time_point - time[bird[i]][1]
                        time_point += time[bird[i]][2]
                    else:
                        time_point = time_point + time[bird[i]][2]
                    
                    line.append(bird[i])
                    d += Demand[bird[i]]
                    i += 1
                else:
                    dis += dis_matrix.loc[line[-1],0]#当前车辆装满
                    line.append(0)
                    dis_sum += dis
                    lines.append(line)
                    #下一辆车
                    dis,d = 0,0
                    line = [0]
                    time_point = 0
        
        #最后一辆车
        dis += dis_matrix.loc[line[-1],0]
        line.append(0)
        dis_sum += dis
        lines.append(line)
        birdPop_car.append(lines)
        fits.append(round(C1*dis_sum+C0*len(lines)+C2*wait+C3*late,1))
        
    return birdPop_car,fits


def crossover(bird,pLine,gLine,w,c1,c2):
    '''
    采用顺序交叉方式;交叉的parent1为粒子本身,分别以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)
    的概率接受粒子本身逆序、当前最优解、全局最优解作为parent2,只选择其中一个作为parent2;
    输入:bird-粒子,pLine-当前最优解,gLine-全局最优解,w-惯性因子,c1-自我认知因子,c2-社会认知因子;
    输出:交叉后的粒子-croBird;
    '''
    croBird = [None]*len(bird)#初始化
    parent1 = bird#选择parent1
    #选择parent2(轮盘赌操作)
    randNum = random.uniform(0, sum([w,c1,c2]))
    if randNum <= w:
        parent2 = [bird[i] for i in range(len(bird)-1,-1,-1)]#bird的逆序
    elif randNum <= w+c1:
        parent2 = pLine
    else:
        parent2 = gLine
    
    #parent1-> croBird
    start_pos = random.randint(0,len(parent1)-1)
    end_pos = random.randint(0,len(parent1)-1)
    if start_pos>end_pos:start_pos,end_pos = end_pos,start_pos
    croBird[start_pos:end_pos+1] = parent1[start_pos:end_pos+1].copy()
    
    # parent2 -> croBird
    list2 = list(range(0,start_pos))
    list1 = list(range(end_pos+1,len(parent2)))
    list_index = list1+list2#croBird从后往前填充
    j = -1
    for i in list_index:
        for j in range(j+1,len(parent2)+1):
            if parent2[j] not in croBird:
                croBird[i] = parent2[j]
                break 
                    
    return croBird


def draw_path(car_routes,CityCoordinates):
    '''
    #画路径图
    输入:line-路径,CityCoordinates-城市坐标;
    输出:路径图
    '''
    for route in car_routes:
        x,y= [],[]
        for i in route:
            Coordinate = CityCoordinates[i]
            x.append(Coordinate[0])
            y.append(Coordinate[1])
        x.append(x[0])
        y.append(y[0])
        plt.plot(x, y,'o-', alpha=0.8, linewidth=0.8)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.show()


if __name__ == '__main__':
    #车辆参数
    CAPACITY = 8#车辆最大容量
    DISTABCE = 1000#车辆最大行驶距离
    V = 40#速度,km/h
    C0 = 100#启动成本
    C1 = 2#行驶成本/km
    C2 = 10#等待成本/h
    C3 = 40#惩罚成本/h
    
    #PSO参数
    birdNum = 50#粒子数量
    w = 0.2#惯性因子
    c1 = 0.4#自我认知因子
    c2 = 0.4#社会认知因子
    pBest,pLine =0,[]#当前最优值、当前最优解,(自我认知部分)
    gBest,gLine = 0,[]#全局最优值、全局最优解,(社会认知部分)
    
    #其他参数
    iterMax = 1000#迭代次数
    iterI = 1#当前迭代次数
    bestfit = [] #记录每代最优值
    
    #读入数据
    Customer = [(70,70),(107,77),(109,139),(120,22),(48,47),(116,22),(12,138),(86,40),(121,124),(61,57),
                (40,113),(129,24),(12,84),(44,116),(102,52),(41,36),(132,138),(104,139),(104,54),(22,104),(46,133)]
    Demand = [0,3.4,0.8,3.9,1.9,3.2,1.4,2.2,2.1,3.5,2.3,1.8,1.6,2.7,1.5,1.3,2.4,2.9,1.3,1.1,0.7]
    time = [(0,10,0),(0.5,4.5,0.2),(2,6.5,0.2),(1,6,0.2),(0.5,6,0.4),(1,6.5,0.2),(3,9,0.5),(0.5,4,0.4),(1.5,6,0.2),(3.5,9,0.2),
                   (1,4.5,0.2),(1.5,6.5,0.4),(1,5.5,0.4),(2,7,0.2),(2.5,6.5,0.2),(3,8,0.2),(2,7,0.4),(2,6,0.2),(0,4.5,0.4),(1,5.5,0.4),(1,5.5,0.4)]
    dis_matrix = calDistance(Customer)#计算城市间距离
    
    
    birdPop = [greedy(Customer,dis_matrix) for i in range(birdNum)]#贪婪算法构造初始解
    # birdPop = [random.sample(range(1,len(Customer)),len(Customer)-1) for i in range(birdNum)]#客户点编码,随机初始化生成种群
    
    birdPop_car,fits = calFitness(birdPop,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1,C2,C3,time,V)#分配车辆,计算种群适应度
    
    gBest = pBest = min(fits)#全局最优值、当前最优值
    gLine = pLine = birdPop[fits.index(min(fits))]#全局最优解、当前最优解
    gLine_car = pLine_car = birdPop_car[fits.index(min(fits))]
    bestfit.append(gBest)
    
    
    while iterI <= iterMax:#迭代开始
        for i in range(birdNum):
            birdPop[i] = crossover(birdPop[i],pLine,gLine,w,c1,c2)
        
        birdPop_car,fits = calFitness(birdPop,Demand,dis_matrix,CAPACITY,DISTABCE,C0,C1,C2,C3,time,V)#分配车辆,计算种群适应度
        pBest,pLine,pLine_car =  min(fits),birdPop[fits.index(min(fits))],birdPop_car[fits.index(min(fits))]
        if min(fits) <= gBest:
            gBest,gLine,gLine_car =  min(fits),birdPop[fits.index(min(fits))],birdPop_car[fits.index(min(fits))]
            
        bestfit.append(gBest)
        print(iterI,gBest)#打印当前代数和最佳适应度值
        iterI += 1#迭代计数加一
        
    print(gLine_car)#路径顺序
    draw_path(gLine_car,Customer)#画路径图
    

4.3 例子求解结果

本文的测试例子借鉴期刊【混合蚁群算法求解双目标时间窗VRP】,具体数据如下:
在这里插入图片描述
车辆启动成本 C0 取100,车辆单位距离行驶成本C1 取2,等待成本取10,惩罚成本取40,车辆最长行驶距离为1000,车辆速度取40,算法运算结果为2616.5,,路径为[[0, 4, 15, 9, 0], [0, 1, 16, 8, 0], [0, 12, 19, 10, 13, 0], [0, 18, 14, 5, 0], [0, 7, 3, 11, 0], [0, 2, 17, 20, 6, 0]] ,路径图如下:
在这里插入图片描述
记录学习过程,欢迎指正

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VRPTW建模与求解—基于粒子群算法 的相关文章

随机推荐

  • 玩转肺癌目标检测数据集Lung-PET-CT-Dx ——③整理、验证数据,建立Dataset对象

    文章目录 数据整理整理出所有的dcm文件整理出所有的xml标注文件整理数据 舍弃错误的标注文件整理数据 两个标注文件指向同一个目标图片的情况封装函数 xff0c 传入xml文件 xff0c 显示标注效果 整理数据 将PETCT的三通道图像转
  • Java_求 1+11+111+1111+11111+111111+1111111+11111111

    span class token keyword package span cn span class token punctuation span myAlgorithm span class token punctuation span
  • Ubuntu22.04安装CUDA、cudnn详细步骤

    文章目录 安装CUDA安装cudnn下载安装文件安装验证是否安装成功 在Ubuntu系统中 xff0c 使用nvidia smi命令可以看到当前GPU信息 xff0c 在右上角可以看到CUDA Version xff0c 意思是最大支持的C
  • Ubuntu22.04部署Pytorch2.0深度学习环境

    文章目录 安装Anaconda创建新环境安装Pytorch2 0安装VS CodeUbuntu下实时查看GPU状态的方法小实验 xff1a Ubuntu Windows10下GPU训练速度对比 Ubuntu安装完显卡驱动 CUDA和cudn
  • 玩转肺癌目标检测数据集Lung-PET-CT-Dx ——④转换成PASCAL VOC格式数据集

    文章目录 关于PASCAL VOC数据集目录结构 创建VOC数据集的几个相关目录XML文件的形式 读取dcm文件与xml文件的配对关系 创建VOC格式数据集 创建训练 验证集 本文所用代码见文末Github链接 关于PASCAL VOC数据
  • Ubuntu与win10 修改开机启动顺序

    lfg 64 lfg ubuntu cd boot grub 打开grub cfg所在文件夹 lfg 64 lfg ubuntu boot grub sudo chmod a 43 w grub cfg 修改权限为所有可写 sudo pas
  • 小觅双目相机标准彩色版SDK的环境配置

    一 初用MYNTEYE双目相机标准彩色版 xff08 SC xff09 1 小觅相机目前标准版有三款 xff0c 详见 xff1a 小觅双目摄像头标准版系列参数比较 2 彩色工程版有以下7种分辨率可选 xff0c 数据输出格式为YUYV x
  • 小觅双目摄像头标准版系列产品参数比较

  • java for无限循环

    for无限循环的几个情况 判断条件为true 会无限循环 省略了判断条件 会无限循环 判断条件为true 会无限循环 package test010 public class Main nbsp nbsp nbsp public stati
  • 计数器与定时器有何区别

    计数器是当你开始从0开始计数时一直不停的开始记数 除非你让他停下来要不他会不停的记下去 而定时器则是不一样的 是需要你自己先设定一个时间然后开始倒计时 当你的所定时间倒计完以后 他就自动停止下来了 懂了吗 至于用哪个就要看你干什么而定了 8
  • C++基础知识

    1 面向对象的程序设计思想是什么 xff1f 答 xff1a 把数据结构和对数据结构进行操作的方法封装形成一个个的对象 2 什么是类 xff1f 答 xff1a 把一些 具有共性的对象归类后形成一个集合 xff0c 也就是所谓的类 3 对象
  • 【开关电源】降压变换器(BUCK)的断续模式建模

    1 前言 在DCDC变换器中BUCK变换器是最基础的一类降压型变换器 xff0c 它可以将输入电压降低后输出 在连续模式CCM下 xff0c 输出和输入之间的比值是D xff08 D为占空比 xff09 这种开关变换器是一种通过电子开关周期
  • 变量命名规范

    本文转载于https blog csdn net ZCF1002797280 article details 51495229 是我见过的描述最精炼 最好懂的命名文档 xff0c 故收藏转载推荐 1 驼峰命名法 1 1 小驼峰法 除第一个单
  • C++实现websocket服务器握手协议(使用Qt)

    前提 xff1a 笔者在开发server程序时 xff0c 要求websocket与server连接 websocket的机制是在第一次连接时进行握手协议 xff0c 协议通过 xff0c 才可以进行正常的通信 xff0c 否则websoc
  • 00011__ARM和STM32的区别

    https blog csdn net qq 34385566 article details 79668280
  • linux中查看系统资源占用情况的命令

    size 61 large top size 主要参数 d xff1a 指定更新的间隔 xff0c 以秒计算 q xff1a 没有任何延迟的更新 如果使用者有超级用户 xff0c 则top命令将会以最高的优先序执行 c xff1a 显示进程
  • 关于PendSV异常和SVC异常

    这里先说什么是异常 xff0c 什么是中断 xff1f 请下这张图 颜色加深的表项为异常 xff0c 这些属于cm3内核自带的 其中 3 xff0c 2 xff0c 1异常的优先级固定 xff0c 是不可更改的 xff0c 其余的异常中断优
  • FreeRTOS学习4-任务创建和删除

    关于任务创建有3个函数 1 动态创建一个任务 可以自动分配任务堆栈和TCB FreeRTOSConfig h中 xff0c 需要定义 define configSUPPORT DYNAMIC ALLOCATION 1 支持动态内存申请 Ba
  • java里 equals和== 区别

    1 java中equals和 61 61 的区别 值类型是存储在内存中的堆栈 xff08 简称栈 xff09 xff0c 而引用类型的变量在栈中仅仅是存储引用类型变量的地址 xff0c 而其本身则存储在堆中 2 61 61 操作比较的是两个
  • VRPTW建模与求解—基于粒子群算法

    VRPTW建模与求解 基于粒子群算法 1 VRPTW简要描述 VRPTW xff08 Vehicle Routing Problem with Time Windows xff09 是指在经典VRP的前提上 xff0c 给每个客户增添时间窗