遗传算法求解时间窗车辆路径规划问题(附python代码)

news2025/2/5 5:56:05

摘要

本研究提出了一种基于遗传算法的车辆路径规划(VRP)问题求解框架,它能够有效地处理一系列复杂约束,包括软时间窗硬时间窗行驶距离限制车辆最大载重量多个配送中心的协调特定的配送顺序以及多种车型的选择。该框架的一个关键特点在于其与其他算法的结合潜力,特别是可以快速与模拟退火、领域搜索等算法快速结合,以便开发出混合优化算法,这一能力将进一步加强搜索效率和解的质量。本文主要介绍算法框架的编码、解码以及交叉变异等内容,并以一个简单实例论证框架的可行性。

VRP问题

问题定义

车辆路径问题(VRP)涉及服务一组客户,每个客户都有某些货物的需求。目标是为一组车辆规划路线,使所有客户都得到服务。核心问题是确定哪辆车应服务哪些客户,以及访问顺序,以最小化总路线成本,这通常是指路线的总长度、总时间或使用的车辆数量。在VRP的最基本形式中,每辆车从配送中心出发,服务一系列客户,然后返回配送中心。每个客户被且只被一辆车服务一次。
在这里插入图片描述

常见约束

(1)容量约束: 每辆车有最大的承载能力,不能超载服务客户。
(2)服务约束: 每辆车分配的路线必须在其工作时长内完成。
(3)时间窗约束: 对于VRP的变体VRPTW,每个客户可能会有一个服务 时间窗口,即服务必须在特定的时间内开始。
(4)车辆数目约束: 可用的车辆数量可能是有限的。
(5)客户需求约束: 每个客户的需求必须得到完全满足。
(6)行驶距离约束:每辆车有最大行驶距离。
(7)配送顺序约束:需求点之间存在配送先后顺序。

常用算法

(1)精确算法: 如线性规划、混合整数规划(使用专门的优化软件如CPLEX、Gurobi)、分支限界等算法,能够求解问题的最优解,但随着问题规模的增加,计算复杂度会迅速上升。
(2)启发式算法: 如节约算法、插入算法、Sweep算法等,它们可以在有限时间内为VRP问题找到较好的解,但不保证最优。
(3)元启发式算法: 如模拟退火、遗传算法、禁忌搜索、蚁群算法、粒子群优化等,这些算法通过迭代搜索来改进解,通常能够产生高质量的近似最优解,适用于大规模问题。
(4)混合启发式: 结合两种或多种启发式或元启发式算法,利用它们各自的优点,以获得更优解。例如,先使用一个启发式算法生成初始解,然后应用另一个不同类型的启发式算法来进一步改进解。

算法设计

遗传算法(Genetic Algorithms, GA)是一种借鉴生物进化过程的全局优化搜索算法,通过自然选择和遗传机制(如交叉、变异)来逐代演化出问题的解。在求解车辆路径问题(Vehicle Routing Problem, VRP)时,遗传算法通过模拟自然选择和遗传机制,提供了一个弹性、高效和鲁棒的途径,适用于各种复杂的变体和实际应用场景。

算子编码

为了支持前文提到的所有约束,本文算子编码采用三段式编码,第一段为需求点遍历顺序的编码,第二段为车辆覆盖范围的编码,第三段为配送点选择的编码。假设对于一个需求点数量为8,车辆数为3,配送中心数量为2的VRP问题,某个个体编码如下。

						[0,1,2,3,4,5,6,7,3,5,8,9,8]

该段编码中[0,1,2,3,4,5,6,7]为需求点遍历顺序编码。[3,5]为车辆覆盖范围编码,即第1辆车覆盖范围为[0,3),第二辆车覆盖范围为[3,5),第三辆车的覆盖范围为[5,8)。[0,1,0]为配送中心编码,表示三辆车分配来自配送点8、9、8。因此,改个体的配送路径为:

								[8,0,1,2,8]
								[9,3,4,9]
								[8,5,6,7,8]

下面是生成种群个体的代码:

'''
生成单个个体
'''
def gen_individual(self):
    # 需求点遍历顺序
    individual=[i for i in range(self.vrp.costomer_num)]
    random.shuffle(individual)
    
    # 车辆分割点
    cur=0
    for i in range(self.car_num-1):
        rnd=randint(cur+1,self.vrp.costomer_num-self.car_num+i+1)
        cur=rnd
        individual.append(cur)
        
    # 仓库选择
    for i in range(self.car_num):
        individual.append(randint(self.vrp.costomer_num,costomer_num+self.vrp.warehouse_num-1))
    
    return self.adjust_individual(individual)
算子交叉

交叉是遗传算法中最重要的操作之一,本文交叉操作采用三个个体两两交叉的方式,三个个体两两交叉,产生的个体数仍然为三,能够有效维持种群稳定性
在这里插入图片描述

'''
三个个体交叉
'''
def triple_cross_individual(self,individual1,individual2,individual3):
    child1=self.double_cross_individual(individual1,individual2)
    child2=self.double_cross_individual(individual1,individual3)
    child3=self.double_cross_individual(individual2,individual3)
    return (child1,child2,child3)

交叉方式分为四步,第一步是需求点遍历顺序的交叉,需求点遍历顺序的交叉为均匀交叉,假设个体1的需求点遍历顺序为[0,1,2,3],个体2需求点的便利顺序为[3,2,1,0],即每次生成一个随机数,根据随机数的取值将个体1和个体2的当前需求点分配给子代1和子代2。第二步是车辆覆盖范围的交叉,采用单次交叉的交叉方式,生成一次随机数,根据随机数的取值将个体1个个体2的编码整段分配给子代1和子代2。第三步是配送中心编码的交叉,配送中心编码的交叉与第一步一直,多次生成随机数,而后将子代1和2的对应编码值按照随机数赋给子代1和子代2。第四步是择优,即选择适应度更优的个体作为最终被接受的子代。

'''
两个个体交叉
'''
def double_cross_individual(self,individual1,individual2):
    child1,child2=[],[]
    
    # 需求点遍历
    for i in range(self.vrp.costomer_num):
        rnd=randint(0,1)
        if rnd:
            if not individual1[i] in child1:
                child1.append(individual1[i])
            else:
                child2.append(individual1[i])
                
            if not individual2[i] in child1:
                child1.append(individual2[i])
            else:
                child2.append(individual2[i])
        else:
            if not individual1[i] in child2:
                child2.append(individual1[i])
            else:
                child1.append(individual1[i])
            if not individual2[i] in child2:
                child2.append(individual2[i])
            else:
                child1.append(individual2[i])
            
    # 车辆切割点
    rnd=randint(0,1)
    if rnd:
        child1+=individual1[self.vrp.costomer_num:self.vrp.costomer_num+self.car_num-1]
        child2+=individual2[self.vrp.costomer_num:self.vrp.costomer_num+self.car_num-1]
    else:
        child1+=individual2[self.vrp.costomer_num:self.vrp.costomer_num+self.car_num-1]
        child2+=individual1[self.vrp.costomer_num:self.vrp.costomer_num+self.car_num-1]
            
    # 仓库选择
    for i in range(self.car_num):
        ii=self.vrp.costomer_num+self.car_num-1+i
        rnd=randint(0,1)
        if rnd:
            child1.append(individual1[ii])
            child2.append(individual2[ii])
        else:
            child1.append(individual2[ii])
            child2.append(individual1[ii])
            
    # 选择更好的child
    child1=self.adjust_individual(child1)
    child2=self.adjust_individual(child2)
    if self.fitness_value(child1)>self.fitness_value(child2):
        return child1
    return child2
算子变异

算子变异是遗传算法的基础操作之一,其目的在于避免种群过早收敛,赋予一定跳出局部最优的能力。本文的算子变异包含两部分,一部分为需求点遍历顺序的变异,即随机选择两个需求点,对其进行位置互换。第二部分为配送中心变异,即随机选择一个配送中心,使其发生变化,转变为从另外一个仓库进行配送。

'''
个体变异
'''
def variate_individual(self,individual):
    copy_individual=deepcopy(individual)
    
    # 需求点遍历顺序变异
    rnd=randint(0,1)
    if rnd:
        p=random.sample(copy_individual[:self.vrp.costomer_num],2)
        temp=copy_individual[p[0]]
        copy_individual[p[0]]=copy_individual[p[1]]
        copy_individual[p[1]]=temp
    
    # 仓库选择变异
    rnd=randint(0,1)
    if rnd:
        idx=randint(0,self.car_num-1)
        copy_individual[self.vrp.costomer_num+self.car_num-1+idx]=\
        randint(self.vrp.costomer_num,costomer_num+self.vrp.warehouse_num-1)
        
    return self.adjust_individual(copy_individual)
适应度函数

适应度是评价个体好坏的标准,本文考虑配送配送距离和时间窗的违背成本,通过加权方式将其结合起来。

'''
计算适应度
'''
def fitness_value(self,individual):
    # 非可行解
    if not individual:
        return 0
    
    # 路径解析
    routes=self.resolve_individual(individual)
    
    # 判断是否可行
    if not self.valid_routes(routes):
        return 0
    
    # 总成本
    total_cost=0
    
    # 距离成本
    for k in routes:
        route=routes[k]
        driving_distance=self.get_driving_distance(route)
        total_cost+=driving_distance
    
    # 时间成本
    time_tables=self.get_time_table(routes)
    for k in time_tables:
        time_table=time_tables[k]
        time_cost=self.get_time_cost(time_table)
        total_cost+=time_cost
        
    return 1/total_cost

实例分析

问题描述

本文以一个单配送中心的VRPTW问题为例,验证本文算法框架的有效性 。该问题描述为共有8个需求点,客户i的需求量为q_i,卸货时间为T_i,客户i要求车辆的到达时间为[ET_i,LT_i],各客户要求的具体数据如下表所示。
在这里插入图片描述
客户与需求点之间的距离如下表所示,在这里0表示配送中心,客户需要的所有需求,均由载重为10吨的车辆来完成,车辆速度为50km每小时,早到和晚到的成本都是50km,即每早到或晚到1h,等同于多走50km的距离。
在这里插入图片描述

结果展示

每代个体30个,迭代代数为100代,迭代结果如下图所示。
在这里插入图片描述

最终结果如下,配送车辆为3,配送距离为1055。

{0: [0, 8, 6, 4, 0], 1: [0, 3, 7, 2, 0], 2: [0, 1, 5, 0]}

本文总结

本研究展示了一种进阶的车辆路径规划(VRP)问题求解框架,重点在于遗传算法的核心应用和扩展性。文中详细阐述了算法的关键组成部分,包括个体编码、解码策略以及交叉和变异机制的设计,确保了可操作性和解的有效性。此外,文章通过一具体实例,证明了该框架不仅在理论上的可行性,同时其实践应用也显示出强大的问题解决能力。最终,本研究为解决复杂的VRP问题提供了一种创新的方法论和方案,预示着在物流及配送问题中的广泛应用前景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis-Plus 查询不到数据,但使用 SQL 可以查询到数据的问题排查

目录 前言 一、问题描述 示例代码 二、排查步骤 1. 检查数据源配置 2. 检查实体类与数据库表结构 3. 检查 Mapper 接口 4. 检查 MyBatis-Plus 配置 5. 排查查询条件 6. 检查日志输出 7. 检查数据库连接问题 8. 检查全局配置和插件 三、解决方案 前言 在开发过程中&…

【docker入门】

在软件开发过程中,环境配置是一个至关重要的步骤,它不仅影响开发效率,也直接关联到软件的最终质量。正确的环境配置可以极大地减少开发中的潜在问题,提升软件发布的流畅度和稳定性。以下是几个关键方面,以及如何优化环…

《窄门》读后感

《窄门》这本书是端午节期间在地铁和高铁上看完的,书的故事很简单,描绘的是一段爱而不得的感情。但是,这本书写的爱而不得和其他地方的爱而不得完全不是一码事,其他地方的爱而不得要么是“落花有意随流水,流水无意恋落…

EasyX 文本输出(自定义)函数报错

EasyX 文本输出(自定义)函数报错记录 原因:EasyX与字符串相关的函数,都有字符集问题 UNICODE 多字节字符集

java干货,spring声明式事务

文章目录 一、编程式事务1.1 什么是编程式事务1.2 编程式事务的优缺点 二、声明式事务2.1 什么是声明式事务2.2 声明式事务的优点2.3 Spring 事务管理器2.4 spring 声明式事务使用 一、编程式事务 1.1 什么是编程式事务 编程式事务是指通过手动编写程序来管理事务&#xff0c…

C#利用SignalR实现通信事例Demo

1.服务端安装SignalR的Nuget包 dotnet add package Microsoft.AspNet.SignalR --version 2.4.3 2.接下来,创建一个ChatHub类,它是SignalR通信的核心: using Microsoft.AspNetCore.SignalR;public class ChatHub : Hub {public static Dict…

苹果mac电脑救星CleanMyMac让我的电脑重获新生!

🎉 发现电脑的救星!CleanMyMac让我的电脑重获新生! CleanMyMac绿色免费版下载如下:记得保存哈,以防失效: https://pan.quark.cn/s/9b08114cf404 CleanMyMac X2024全新版下载如下: https://wm.makeding.…

awdawdad

作者主页: 作者主页 本篇博客专栏:C 创作时间 :2024年6月20日 最后: 十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我: 1.一个冷知识: …

多路h265监控录放开发-(9)通过拖拽到窗口完成渲染

xcamera_widget.h class XCameraWidget :public QWidget {Q_OBJECTpublic:XCameraWidget(QWidget* p nullptr);//渲染视频void Draw();//123//清理资源,再一个窗口被覆盖后 清理之前窗口生成的资源1~XCameraWidget();//123 private:XDecodeTask* decode_ nullptr;//123XDemu…

高考填报志愿选专业,要善于发掘自身优势

每年的高考季,如何填报志愿又再成为困扰家长以及学生的难题,可能在面对大量的专业时,无论是考生还是家长都不知道应该如何选择,好的专业孩子不一定有优势,感兴趣的冷门专业又担心日后找工作难。 实际上,专业…

购买服务器,并安装宝塔

前言: 我们在开发项目时,总会遇到一个问题,就是将我们开发好的项目上传的公网中。对于中小型的项目,我们可以通过购买服务器进行项目的上线。 我们的项目一般是部署在Linux环境中。如果你不是专业的运维人员,可能对于…

『这世界上有无忧无虑的孩子,和永远焦虑的父母』

昨天,准确说是今天,凌晨两点多,被队友薅起来,严肃认真地讨论孩子的教育问题。 我们家的小神兽六岁,一年级了。从去年幼升小的阶段,我们就计划着好好培养孩子,在这一年间,给小朋友报过…

Linux下VSCode的安装和基本使用

应用场景:嵌入式开发。 基本只需要良好的编辑环境,能支持文件搜索和跳转,就挺OK的。 之所以要在Linux下安装,是因为在WIN11上安装后,搜索功能基本废了,咋弄都弄不好,又不方便重装win系统&#x…

HTTP网络协议

1.HTTP (1)概念: Hyper Text Transfer Protocol,超文本传输协议规定了浏览器和服务器之间数据传输的规则。 (2)特点 基于TCP协议:面向连接,安全基于请求-响应模型的:一次请求对应一次响应HTTP协…

2.APP测试-安卓adb抓取日志

1.打开手机的开发者模式,打开USB调试 (1)小米手机打开开发者模式: 【设置】-【我的设备】-【全部参数信息】-快速多次点击【OS版本】-进入开发者模式 (2)连接手机和电脑,手机打开USB调试 【设置…

“论软件系统建模方法”必过范文,软考高级,系统架构设计师论文

论文真题 软件系统建模(Software System Modeling)是软件开发中的重要环节,通过构建软件系统模型可以帮助系统开发人员理解系统、抽取业务过程和管理系统的复杂性,也可 以方便各类人员之间的交流。软件系统建模是在系统需求分析和系统实现之间架起的一 座桥梁,系统开发人…

Linux服务升级:Almalinux 升级 WebCatlog桌面程序

目录 一、实验 1.环境 2.Almalinux 升级 WebCatlog桌面程序 二、问题 1.Ubuntu如何升级 WebCatlog桌面程序 一、实验 1.环境 (1)主机 表1 主机 系统版本软件IP备注Almalinux9.4 WebCatlog 192.168.204.150 (2)Termi…

【物联网】NB-IoT

目录 一、什么是NBIOT 二、NB-IoT的特点 三、NBIOT的工作状态 四、移远NB-IoT模块及AT指令 一、什么是NBIOT NB-IoT(Narrow Band Internet of Things)窄带物联网,构建于蜂窝网络,所占用的带宽很窄,只需约180KHz&am…

mysql的安装和连接

一.数据库相关概 念 1.数据库 存储数据的仓库,数据是有组织的进行存储,简称DB。 2.数据库管理系统 操纵和管理数据库的大型软件,简称DBM。 3.SQL 操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。简称SQL。 二.市面上流行的数据库 1.ORACLE 2.MySQL …

定个小目标之刷LeetCode热题(27)

这道题,我们可以使用动态规划,假设数组元素范围是>0,那么递推关系式表示为 imax MAX(imax * nums[i], nums[i]),由于本题数组元素范围是整数,即存在负数,最大可能变最小,最小可能变最大,所…