VRPTW

2023-10-31

Python解决VRPTW问题



一、VRPTW问题是什么?

VRP问题一般是指从物流配送中心用多辆车向多个客户需求点送货,每个客户需求点的位置和需求量是已知的,车辆的最大载质量是一定的,要求合理安排配送路线使得目标函数最优,并且满足以下条件:每个客户需求点只能被一辆车访问,并且只能访问一次;每辆车都是从配送中心出发,最后回到配送中心;每个客户需求点的需求量必须满足。
带时间窗的VRP问题是在该基础上,还需要满足服务时间约束,即配送人员需要在一定的时间窗内到达客户需求点进行服务。带时间窗的VRP问题分为硬时间窗和软时间窗两种,硬时间窗是车辆一定要在时间窗范围内到达指定用户处,不能早于时间窗要求的最早时间,也不能晚于时间窗的最晚时间;软时间窗是允许车辆早于或晚于时间窗限制到达用户处,但是早于时间窗的最早时间到达需要增加一个机会损失成本,晚于时间窗的最晚时间到达需要增加一个迟到的惩罚成本。

二、Python代码解决VRPTW问题

2.1 引入库

代码如下(示例):

import random, math, sys
import matplotlib.pyplot as plt 
from copy import deepcopy
from tqdm import *  

导入Python依赖库,matplotlib库是用来画图的,tqdm库是用来显示进度条的

2.2 参数的设置

代码如下(示例):

DEBUG = False
sampleSolution = [0, 1, 20, 9, 3, 12, 26, 25, 24, 4, 23, 22, 21, 0, 13, 2, 15, 14, 6, 27, 5, 16, 17, 8, 18, 0, 7, 19,
                  11, 10, 0]
geneNum = 100  # 种群数量
generationNum = 3000  # 迭代次数

CENTER = 0  # 配送中心

HUGE = 9999999
VARY = 0.05  # 变异几率

n = 25  # 客户点数量
m = 2  # 换电站数量
k = 3  # 车辆数量
Q = 5  # 额定载重量, t
dis = 160  # 续航里程, km
costPerKilo = 10  # 油价
epu = 20  # 早到惩罚成本
lpu = 30  # 晚到惩罚成本
speed = 40  # 速度,km/h
# 坐标
X = [56, 66, 56, 88, 88, 24, 40, 32, 16, 88, 48, 32, 80, 48, 23, 48, 16, 8, 32, 24, 72, 72, 72, 88, 104, 104, 83, 32]
Y = [56, 78, 27, 72, 32, 48, 48, 80, 69, 96, 96, 104, 56, 40, 16, 8, 32, 48, 64, 96, 104, 32, 16, 8, 56, 32, 45, 40]
# 需求量
t = [0, 0.2, 0.3, 0.3, 0.3, 0.3, 0.5, 0.8, 0.4, 0.5, 0.7, 0.7, 0.6, 0.2, 0.2, 0.4, 0.1, 0.1, 0.2, 0.5, 0.2, 0.7, 0.2,
     0.7, 0.1, 0.5, 0.4, 0.4]
# 最早到达时间
eh = [0, 0, 1, 2, 7, 5, 3, 0, 7, 1, 4, 1, 3, 0, 2, 2, 7, 6, 7, 1, 1, 8, 6, 7, 6, 4, 0, 0]
# 最晚到达时间
lh = [100, 1, 2, 4, 8, 6, 5, 2, 8, 3, 5, 2, 4, 1, 4, 3, 8, 8, 9, 3, 3, 10, 10, 8, 7, 6, 100, 100]
# 服务时间
h = [0, 0.2, 0.3, 0.3, 0.3, 0.3, 0.5, 0.8, 0.4, 0.5, 0.7, 0.7, 0.6, 0.2, 0.2, 0.4, 0.1, 0.1, 0.2, 0.5, 0.2, 0.7, 0.2,
     0.7, 0.1, 0.5, 0.4, 0.4]

2.3 算法部分

class Gene:
    def __init__(self, name='Gene', data=None):
        self.name = name
        self.length = n + m + 1
        if data is None:
            self.data = self._getGene(self.length)
        else:
            assert(self.length+k == len(data))
            self.data = data
        self.fit = self.getFit()
        self.chooseProb = 0  # 选择概率

    # randomly choose a gene
    def _generate(self, length):
        data = [i for i in range(1, length)]
        random.shuffle(data)
        data.insert(0, CENTER)
        data.append(CENTER)
        return data

    # insert zeors at proper positions
    def _insertZeros(self, data):
        sum = 0
        newData = []
        for index, pos in enumerate(data):
            sum += t[pos]
            if sum > Q:
                newData.append(CENTER)
                sum = t[pos]
            newData.append(pos)
        return newData

    # return a random gene with proper center assigned
    def _getGene(self, length):
        data = self._generate(length)
        data = self._insertZeros(data)
        return data

    # return fitness
    def getFit(self):
        fit = distCost = timeCost = overloadCost = fuelCost = 0
        dist = []  # from this to next

        # calculate distance
        i = 1
        while i < len(self.data):
            calculateDist = lambda x1, y1, x2, y2: math.sqrt(((x1 - x2) ** 2) + ((y1 - y2) ** 2))
            dist.append(calculateDist(X[self.data[i]], Y[self.data[i]], X[self.data[i - 1]], Y[self.data[i - 1]]))
            i += 1

        # distance cost
        distCost = sum(dist) * costPerKilo

        # time cost
        timeSpent = 0
        for i, pos in enumerate(self.data):
            # skip first center
            if i == 0:
                continue
            # new car
            elif pos == CENTER:
                timeSpent = 0
            # update time spent on road
            timeSpent += (dist[i - 1] / speed)
            # arrive early
            if timeSpent < eh[pos]:
                timeCost += ((eh[pos] - timeSpent) * epu)
                timeSpent = eh[pos]
            # arrive late
            elif timeSpent > lh[pos]:
                timeCost += ((timeSpent - lh[pos]) * lpu)
            # update time
            timeSpent += h[pos]

        # overload cost and out of fuel cost
        load = 0
        distAfterCharge = 0
        for i, pos in enumerate(self.data):
            # skip first center
            if i == 0:
                continue
            # charge here
            if pos > n:
                distAfterCharge = 0
            # at center, re-load
            elif pos == CENTER:
                load = 0
                distAfterCharge = 0
            # normal
            else:
                load += t[pos]
                distAfterCharge += dist[i - 1]
                # update load and out of fuel cost
                overloadCost += (HUGE * (load > Q))
                fuelCost += (HUGE * (distAfterCharge > dis))

        fit = distCost + timeCost + overloadCost + fuelCost
        return 1/fit

    def updateChooseProb(self, sumFit):
        self.chooseProb = self.fit / sumFit

    def moveRandSubPathLeft(self):
        path = random.randrange(k)  # choose a path index
        index = self.data.index(CENTER, path+1) # move to the chosen index
        # move first CENTER
        locToInsert = 0
        self.data.insert(locToInsert, self.data.pop(index))
        index += 1
        locToInsert += 1
        # move data after CENTER
        while self.data[index] != CENTER:
            self.data.insert(locToInsert, self.data.pop(index))
            index += 1
            locToInsert += 1
        assert(self.length+k == len(self.data))

    # plot this gene in a new window
    def plot(self):
        Xorder = [X[i] for i in self.data]
        Yorder = [Y[i] for i in self.data]
        plt.plot(Xorder, Yorder, c='black', zorder=1)
        plt.scatter(X, Y, zorder=2)
        plt.scatter([X[0]], [Y[0]], marker='o', zorder=3)
        plt.scatter(X[-m:], Y[-m:], marker='^', zorder=3)
        plt.title(self.name)
        plt.show()


def getSumFit(genes):
    sum = 0
    for gene in genes:
        sum += gene.fit
    return sum


# return a bunch of random genes
def getRandomGenes(size):
    genes = []
    for i in range(size):
        genes.append(Gene("Gene "+str(i)))
    return genes


# 计算适应度和
def getSumFit(genes):
    sumFit = 0
    for gene in genes:
        sumFit += gene.fit
    return sumFit


# 更新选择概率
def updateChooseProb(genes):
    sumFit = getSumFit(genes)
    for gene in genes:
        gene.updateChooseProb(sumFit)


# 计算累计概率
def getSumProb(genes):
    sum = 0
    for gene in genes:
        sum += gene.chooseProb
    return sum


# 选择复制,选择前 1/3
def choose(genes):
    num = int(geneNum/6) * 2    # 选择偶数个,方便下一步交叉
    # sort genes with respect to chooseProb
    key = lambda gene: gene.chooseProb
    genes.sort(reverse=True, key=key)
    # return shuffled top 1/3
    return genes[0:num]


# 交叉一对
def crossPair(gene1, gene2, crossedGenes):
    gene1.moveRandSubPathLeft()
    gene2.moveRandSubPathLeft()
    newGene1 = []
    newGene2 = []
    # copy first paths
    centers = 0
    firstPos1 = 1
    for pos in gene1.data:
        firstPos1 += 1
        centers += (pos == CENTER)
        newGene1.append(pos)
        if centers >= 2:
            break
    centers = 0
    firstPos2 = 1
    for pos in gene2.data:
        firstPos2 += 1
        centers += (pos == CENTER)
        newGene2.append(pos)
        if centers >= 2:
            break
    # copy data not exits in father gene
    for pos in gene2.data:
        if pos not in newGene1:
            newGene1.append(pos)
    for pos in gene1.data:
        if pos not in newGene2:
            newGene2.append(pos)
    # add center at end
    newGene1.append(CENTER)
    newGene2.append(CENTER)
    # 计算适应度最高的
    key = lambda gene: gene.fit
    possible = []
    while gene1.data[firstPos1] != CENTER:
        newGene = newGene1.copy()
        newGene.insert(firstPos1, CENTER)
        newGene = Gene(data=newGene.copy())
        possible.append(newGene)
        firstPos1 += 1
    possible.sort(reverse=True, key=key)
    assert(possible)
    crossedGenes.append(possible[0])
    key = lambda gene: gene.fit
    possible = []
    while gene2.data[firstPos2] != CENTER:
        newGene = newGene2.copy()
        newGene.insert(firstPos2, CENTER)
        newGene = Gene(data=newGene.copy())
        possible.append(newGene)
        firstPos2 += 1
    possible.sort(reverse=True, key=key)
    crossedGenes.append(possible[0])


# 交叉
def cross(genes):
    crossedGenes = []
    for i in range(0, len(genes), 2):
        crossPair(genes[i], genes[i+1], crossedGenes)
    return crossedGenes



# 合并
def mergeGenes(genes, crossedGenes):
    # sort genes with respect to chooseProb
    key = lambda gene: gene.chooseProb
    genes.sort(reverse=True, key=key)
    pos = geneNum - 1
    for gene in crossedGenes:
        genes[pos] = gene
        pos -= 1
    return  genes


# 变异一个
def varyOne(gene):
    varyNum = 10
    variedGenes = []
    for i in range(varyNum):
        p1, p2 = random.choices(list(range(1,len(gene.data)-2)), k=2)
        newGene = gene.data.copy()
        newGene[p1], newGene[p2] = newGene[p2], newGene[p1] # 交换
        variedGenes.append(Gene(data=newGene.copy()))
    key = lambda gene: gene.fit
    variedGenes.sort(reverse=True, key=key)
    return variedGenes[0]


# 变异
def vary(genes):
    for index, gene in enumerate(genes):
        # 精英主义,保留前三十
        if index < 30:
            continue
        if random.random() < VARY:
            genes[index] = varyOne(gene)
    return genes

2.4 主函数

if __name__ == "__main__" and not DEBUG:
    genes = getRandomGenes(geneNum) # 初始种群
    # 迭代
    for i in tqdm(range(generationNum)):
        updateChooseProb(genes)
        sumProb = getSumProb(genes)
        chosenGenes = choose(deepcopy(genes))   # 选择
        crossedGenes = cross(chosenGenes)   # 交叉
        genes = mergeGenes(genes, crossedGenes) # 复制交叉至子代种群
        genes = vary(genes) # under construction
    # sort genes with respect to chooseProb
    key = lambda gene: gene.fit
    genes.sort(reverse=True, key=key)   # 以fit对种群排序
    print('\r\n')
    print('data:', genes[0].data)
    print('fit:', genes[0].fit)
    genes[0].plot() # 画出来
if DEBUG:
    print("START")
    gene = Gene()
    print(gene.data)
    gene.moveRandSubPathLeft()
    print(gene.data)
    print("FINISH")

三、数据集和显示的结果图

3.1 数据集

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

3.2 求解结果

在这里插入图片描述
VRPTW的求解结果:
在这里插入图片描述


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

VRPTW 的相关文章

  • 类的 IPython 表示

    我正在使用我创建的模块尝试 IPython 但它没有显示类对象的实际表示 相反 它显示类似的内容 TheClass module TheClass name I heavily在这个模块中使用元类 我有真正有意义的类表示 应该向用户显示 是
  • Pandas set_levels,如何避免标签排序?

    我使用时遇到问题set levels多索引 from io import StringIO txt Name Height Age Metres A 1 25 B 95 1 df pd read csv StringIO txt heade
  • Python - 比较同一字典中的值

    我有一本字典 d Trump MAGA FollowTheMoney Clinton dems Clinton Stein FollowTheMoney Atlanta 我想删除字符串列表中的重复字符串 该字符串是键的值 对于这个例子 期望
  • Gunicorn 工作人员无论如何都会超时

    我正在尝试通过gunicorn运行一个简单的烧瓶应用程序 但是无论我做什么 我的工作人员都会超时 无论是否有针对应用程序的活动 工作人员在我设置任何内容后总是会超时timeout值到 是什么导致它们超时 当我发出请求时 请求成功通过 但工作
  • 如何在 Matplotlib 饼图周围绘制箭头以将每个标签指向圆圈中各自的部分?

    我一直在用 Matplotlib 绘制一些图表 我有一个饼图 想要在图表周围绘制箭头 使每个标签都指向图表 我有一个例子 这是我当前的代码 import matplotlib pyplot as plt plt rcParams font
  • NLTK 2.0分类器批量分类器方法

    当我运行此代码时 它会抛出一个错误 我认为这是由于 NLTK 3 0 中不存在batch classify 方法 我很好奇如何解决旧版本中的某些内容在新版本中消失的此类问题 def accuracy classifier gold resu
  • 我应该使用 Python 双端队列还是列表作为堆栈? [复制]

    这个问题在这里已经有答案了 我想要一个可以用作堆栈的 Python 对象 使用双端队列还是列表更好 元素数量较少还是数量较多有什么区别 您的情况可能会根据您的应用程序和具体用例而有所不同 但在一般情况下 列表非常适合堆栈 append is
  • 在 Django Admin 中调整字段大小

    在管理上添加或编辑条目时 Django 倾向于填充水平空间 但在某些情况下 当编辑 8 个字符宽的日期字段或 6 或 8 个字符的 CharField 时 这确实是一种空间浪费 字符宽 然后编辑框最多可容纳 15 或 20 个字符 我如何告
  • Pycharm 在 os.path 连接上出现“未解析的引用”

    将pycharm升级到2018 1 并将python升级到3 6 5后 pycharm报告 未解析的引用 join 最新版本的 pycharm 不会显示以下行的任何警告 from os path import join expanduser
  • 打印数字时添加千位分隔符[重复]

    这个问题在这里已经有答案了 我真的不知道这个问题的 名称 所以它可能是一个不正确的标题 但问题很简单 如果我有一个数字 例如 number 23543 second 68471243 我想要它使print 像这样 23 54368 471
  • 矩形函数的数值傅里叶变换

    本文的目的是通过一个众所周知的分析傅里叶变换示例来正确理解 Python 或 Matlab 上的数值傅里叶变换 为此 我选择矩形函数 这里报告了它的解析表达式及其傅立叶变换https en wikipedia org wiki Rectan
  • 导入错误:没有名为flask.ext.login的模块

    我的flask login 模块有问题 我已经成功安装了flask login模块 另外 从命令提示符我可以轻松运行此脚本 不会出现错误 Python 2 7 r27 82525 Jul 4 2010 07 43 08 MSC v 1500
  • 未知错误:Chrome 无法启动:异常退出

    当我使用 chromedriver 对 Selenium 运行测试时 出现此错误 selenium common exceptions WebDriverException Message unknown error Chrome fail
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序
  • pandas - 包含时间序列数据的堆积条形图

    我正在尝试使用时间序列数据在 pandas 中创建堆积条形图 DATE TYPE VOL 0 2010 01 01 Heavy 932 612903 1 2010 01 01 Light 370 612903 2 2010 01 01 Me
  • Pandas 组合不同索引的数据帧

    我有两个数据框df 1 and df 2具有不同的索引和列 但是 有一些索引和列重叠 我创建了一个数据框df索引和列的并集 因此不存在重复的索引或列 我想填写数据框df通过以下方式 for x in df index for y in df
  • 每当使用 import cv2 时 OpenCV 都会出错

    我在终端上使用 pip3 install opencv contrib python 安装了 cv2 并且它工作了 但是每当我尝试导入 cv2 或运行导入了 cv2 的 vscode 文件时 在 python IDLE 上它都会说 Trac
  • 制作一份 Python 文档的 PDF 文件

    Python 官方网站提供 PDF 文档下载 但它们是按章节分隔的 我下载了源代码并构建了 PDF 文档 这些文档也是单独的 PDF 我怎么能够从源代码中的 Makefile 构建一个 PDF 文件 我认为这样阅读起来会更方便 如果连接单独
  • pandas.read_csv 将列名移动一倍

    我正在使用位于的 ALL zip 文件here http www fec gov disclosurep PDownload do 我的目标是用它创建一个 pandas DataFrame 但是 如果我跑 data pd read csv
  • pytest找不到模块[重复]

    这个问题在这里已经有答案了 我正在关注pytest 良好实践 https docs pytest org en latest explanation goodpractices html test discovery或者至少我认为我是 但是

随机推荐

  • 解决freemarker数组中的对象属性获取不到

    1 问题现象 使用Freemarker写入模板的时候 遍历List的时候发现对象中的首字母大写和带下划线的时候就会报错 The following has evaluated to null or missing FTL stack tra
  • 基于Rockchip RK3588 Android12 SDK搭建自己的repo 仓库服务器

    基于Rockchip RK3588 Android12 SDK搭建自己的repo 仓库服务器 文章目录 基于Rockchip RK3588 Android12 SDK搭建自己的repo 仓库服务器 搭建自己的repo代码服务器 流程框图 环
  • Markdown自定义CSS样式

    前言 当我第一次接触到Markdown时 我就深深爱上了它 这简洁的界面 编程式的书写都令我爱不释手 最重要的是 还能够支持自定义html css 自定义CSS样式 说到Markdown 就不得不提及Typora这个软件 本例子即是在此软件
  • 解决vue3类型“{}”上不存在属性

    刚创建的一个Vue3和Ts的项目 结果使用Vscode打开后 修改了index vue文件就报错了 修改tsconfig json文件 在tsconfig json文件中添加一行代码 就是让ts识别vue文件 include src ts
  • Ubuntu虚拟机中网络中没有网卡

    由于断电等异常操作 导致vmware的ubuntu系统连接不到网络 ping www baidu com 提示 name or service not known 查看网卡配置 vi etc network interfaces 结果发现只
  • Circular placeholder reference 'server.port' in property definitions

    Exception in thread main java lang IllegalArgumentException Circular placeholder reference server port in property defin
  • Cannot run program "scripts\saveVersion.sh"

    用Maven 编译hadoop遇到以下错误 saveVersion sh script fails in windows cygwin hadoop yarn common 半天是个bug 解决方案如下 Index hadoop mapre
  • C++常用经典算法总结

    一 算法概述 排序算法可以分为两大类 非线性时间比较类排序 通过比较来决定元素间的相对次序 由于其时间复杂度不能突破O nlogn 因此称为非线性时间比较类排序 线性时间非比较类排序 不通过比较来决定元素间的相对次序 它可以突破基于比较排序
  • C#如何通过存储过程从数据库中获得数据

    存储过程就是在数据库中写好的函数 C 通过调用存储过程来获得数据 可以在一定程度上提高数据库的安全性 将一些重要的数据封装了起来 那么如何在C 中调用存储过程呢 一 存储过程 环境如下 1 数据库Itcast2014中包含表TblStude
  • VS的C++项目添加LAPACK库简便方法(注:64位+32位,且不用自己编译库)

    需要材料 1 已经编译好的库文件 dll文件和头文件 http icl cs utk edu lapack for windows lapack libraries 这个网站中有已经用minGW编译好的LAPACK库 lib 一共有三个 除
  • 实践DIV+CSS网页布局入门指南

    实践DIV CSS网页布局入门指南 你正在学习CSS布局吗 是不是还不能完全掌握纯CSS布局 通常有两种情况阻碍你的学习 第一种可能是你还没有理解CSS处理页面的原理 在你考虑你的页面整体表现效果前 你应当先考虑内容的语义和结构 然后再针对
  • uniapp使用jsZip打包多个url文件,下载为一个压缩包

    1 需求及前言 可选中多个文件 类型不限png doc xls ppt等 点击下载时 将选中的文件全部打包成一个压缩包给用户 本文讲解jszip这个插件的打包下载使用方法 2 下载插件 npm install file saver npm
  • kafka服务端常见报错

    打印错误ERROR日志 cat kafkaserver log grep i A3 ERROR 日志目录 1 x data 2 x data logs kedacom project namespace dol kafka dol kafk
  • c++四内存区

    c 程序执行时 内存分为四个区域 1 代码区 存放函数体的二进制代码 由操作系统管理 2 全局区 存放全局变量 静态变量和常亮 3 栈区 编译器自动分配释放 存放函数的参数和局部变量等 4 堆区 程序员分配和释放 若未释放 程序结束时有操作
  • # 关于idea中模块文件夹右下角没有蓝色小方块,pom文件显示橘色

    关于idea中模块文件夹右下角没有蓝色小方块 pom文件显示橘色 模块文件夹中右下角没有蓝色小方块 根本原因是因为模块文件夹中没有xxx iml文件 这个本人亲自试验过 将xxx iml文件删除后 模块文件夹右下角小蓝块立马消失 可以参考下
  • 玩好go的切片

    go的slice 入门就会遇到 但这个东西大多数人都是停留在简单的使用 一些干了好几年的老程序员都说不明白里面的道道 这里面坑不少 恰巧今天有空 好好整理下 永不踩坑 1 为什么要用切片 其他语言大多用的都是数组 在go中 数组的长度是不可
  • 尝试构建知识体系

    1 构建知识体系架构是需要 深入 广知 思考 整理 深入 需要反反复复 学致用 用致学 深度思考 锤炼打磨 不同角度不同方式去尝试思考 实践 广知 需要周围东西的敏感度 好学 求知 充满兴趣 我们积累的知识 能否形成体系 却依赖于我们能否做
  • detectron2的结构介绍及代码实现

    detectron2的结构介绍 上一篇文章 detectron2的简介和配置 d948142375的博客 CSDN博客 介绍了怎么配置detectron2 以下简称DET2 到一台ubuntu18 04的远程服务器 本文将介绍为了实现一个基
  • ResNet之残差结构的理解

    ResNet 论文 2015年提出的ResNet 2016年改进后的ResNet 博客 本人实现的2015 2016的ResNet网络复现 深度学习 残差resnet网络原理详解 ResNet详解 通俗易懂版 主干网络系列 2 ResNet
  • VRPTW

    Python解决VRPTW问题 文章目录 Python解决VRPTW问题 一 VRPTW问题是什么 二 Python代码解决VRPTW问题 2 1 引入库 2 2 参数的设置 2 3 算法部分 2 4 主函数 三 数据集和显示的结果图 3