如何写代码实现VRP问题中车辆容量限制及时间窗要求(python)

news2024/11/9 2:14:39

问题研究背景

使用遗传模拟退火算法求解如下10个卸货点的VRPTW问题。为了使研究的问题更加有意义,本人将时间限理解为服务点一天的具体可以允许配送的时间。 如果不要求车辆从配送中心出发的时间是统一的并且为0时刻,那么就默认第一个配送节点是一定能赶到的。采取从配送中心出发的时间不为0时刻的策略,默认一定能达到第一个配送点,所以采用最早到达时间推算车辆出发的时间。
假设配送中心营业时间是早上七点至晚上七点,即配送中心也有最早和最晚时间窗要求,车辆配送货物应该满足这个发车即回到配送中心的最晚时间限制。卸货点1-10的时间限制理解如下:卸货点1要求在下午1点至下午4点配送,卸货点1要求的服务时间是半个小时;卸货点2要求在下午4点至下午6点配送,卸货点2要求的服务时间是1个小时,以此类推其他的卸货点的配送及服务时间限制。算法中用到配送及服务时间是下午的情况,例如卸货点1可转成数字表示是[13,16]。

在这里插入图片描述

配送点的需求货物量如下:

在这里插入图片描述

配送点的达到时间窗及服务时间如下:

在这里插入图片描述

代码编码思路

取染色体,依次判断染色体的基因是否满足车辆载重量及时间窗限制条件,染色体基因片段如果不满足两者,则默认为一条路线,在中间插入配送中心节点0.

考虑是否可以写两个独立的函数,先判断车辆的载重量限制,再前面生成的解再次寻优判断是否满足时间窗限制。

编写代码过程中遇到的错误

从配送中心出发立即回到配送中心

chrom [10  1  5  2  3  6  4  9  7  8]
*******000******
*******000******
routes [[0, 0, 0]]

当首次配送的需求点为卸货点10时,最早到达时间要求是下午5点,配送中心开门是上午七点,关门是下午七点,两点之间的路径长度是160公里,车辆每小时的车速是40公里/小时,所以最佳的方案是不考虑先去卸货点10完成配送任务,因为车辆返回时赶不上配送中心的关门时间。

在这里插入图片描述

一些其他的错误

opulation [[ 7  6  1  2  9  3  4  5  8 10]
 [ 3  2  5 10  7  4  6  8  1  9]
 [ 4  6  8  7  1  9  5  3  2 10]
 [10  5  7  2  6  4  3  9  8  1]
 [ 9  7 10  8  2  1  4  5  3  6]
 [ 5 10  9  3  6  1  2  4  8  7]
 [ 5  6  2  7  3 10  9  4  8  1]
 [ 2  9  1  3 10  8  6  4  5  7]
 [ 7  3  1  6  2 10  9  8  4  5]
 [10  4  5  9  6  7  3  2  1  8]
 [ 2  4  3  5  8  6  7  1 10  9]
 [ 9  2  6  8  3  1  5  4 10  7]
 [10  2  9  5  1  4  6  3  8  7]
 [ 4  9  5  2  6  1 10  3  8  7]
 [ 7  4  6  8  9 10  3  2  1  5]
 [10  1  4  9  6  2  3  5  7  8]
 [10  9  5  4  3  2  8  1  7  6]
 [ 7  3  8  1 10  5  4  2  9  6]
 [ 3  9 10  4  6  7  5  2  1  8]
 [ 5 10  3  6  4  7  9  1  2  8]
 [ 5  7  3  6  1  2  4  9 10  8]
 [ 3  9  1 10  5  4  2  7  6  8]
 [10  7  1  2  5  8  6  9  4  3]
 [10  6  8  2  9  7  4  5  1  3]
 [ 4  2  7  1  9  3 10  5  8  6]
 [ 7  4  5  8  1  3  9  6 10  2]
 [ 4  1  7  5  9  2  3 10  8  6]
 [ 5  3  1 10  8  9  7  6  4  2]
 [ 7  3  4  5  9  6  8  1 10  2]
 [ 4  2  5 10  1  9  6  7  8  3]
 [ 1  6  4  2 10  7  3  8  9  5]
 [ 9  4  3  6  8 10  2  1  7  5]
 [ 4  7  2  3  9 10  1  5  6  8]
 [ 5  6 10  8  9  7  2  1  3  4]
 [ 8  3  9  1  6  5  4 10  7  2]
 [ 5  7  4  9  3  8 10  1  2  6]
 [ 7  2  9  1  6  5  4 10  3  8]
 [ 6 10  4  5  8  7  1  3  9  2]
 [ 9  5 10  8  3  6  7  2  1  4]
 [ 5  6  3 10  4  9  8  7  1  2]
 [ 7  1  8  6  2  3  9  5 10  4]
 [ 9  1  8  7  4  3  2  6 10  5]
 [ 7  3  2 10  1  6  4  9  8  5]
 [ 5  9  6  3  7  2  8  4  1 10]
 [ 1  2  4  7  8  5  3  6  9 10]
 [ 3  7  2  1  6 10  5  9  4  8]
 [ 7  5  9  3  8  4 10  2  1  6]
 [ 5  6  8 10  9  3  7  4  1  2]
 [ 3  9  7  6  5  2 10  1  4  8]
 [ 3  4  2  7  1  9  8  5 10  6]]
chrom [ 7  6  1  2  9  3  4  5  8 10]
*******000******
total_path_list [[0, 7, 6, 0], [0, 1, 2, 9, 0], [0, 3, 0], [0, 4, 5, 8, 0], [0, 10, 0]]
node 2
node 3
new_chrom [2, 3, 0, 9, 0, 0]
*******000******
total_path_list [[0, 0, 9, 0]]
new_chrom [9]
*******000******
total_path_list [[0, 9, 0]]
node 9
new_chrom [9]
routes [9]

cannotbe_firstnode_served [4, 5, 7, 10]
*******000******
total_path_list [[0, 5, 1, 2, 0], [0, 10, 0], [0, 4, 6, 0], [0, 3, 0], [0, 7, 8, 0], [0, 9, 0]]
path_list [0, 5, 1, 2, 0]
path_list [0, 10, 0]
path_list [0, 4, 6, 0]
path_list [0, 3, 0]
path_list [0, 7, 8, 0]
path_list [0, 9, 0]
new_chrom [3, 9, 5, 10, 4, 7]
*******000******
total_path_list [[0, 10, 0], [0, 4, 0], [0, 5, 0], [0, 7, 0]]
total_path_list [[0, 2, 3, 0], [0, 4, 5, 0], [0, 6, 7, 0], [0, 8, 9, 0], [0, 10, 0], [0, 1, 0]]
path_list [0, 2, 3, 0]
path_list [0, 4, 5, 0]
path_list [0, 6, 7, 0]
path_list [0, 8, 9, 0]
path_list [0, 10, 0]
path_list [0, 1, 0]
feasible_node_list [2, 3, 6, 8, 9, 1]
not_feasible_node_list [4, 5, 7, 10]
new_chrom [2, 3, 6, 8, 9, 1, 4, 5, 7, 10]

Process finished with exit code 0

函数代码

修改卸货点的时间窗,增加求得时间窗+车辆载重量约束限制的可行解概率。
在这里插入图片描述

车辆容量限制的代码见本博主的博文《【纠错】遗传算法求解VRP计算车辆容量限制的代码有bug》,时间窗要求的函数如下:

def time_window_restraint(total_path_list):
    # 先求解车辆容量限制,再计算时间窗限制,硬时间窗限制
    # 如果不要求车辆从配送中心出发的时间是统一的并且为0时刻,那么就默认第一个配送节点是一定能赶到的
    # 采取从配送中心出发的时间不为0时刻的策略,默认一定能达到第一个配送点,所以采用最早到达时间推算车辆出发的时间
    # 假设配送中心营业时间是早上七点至晚上七点
    # 先排除算例无解的场景,即配送中心开门时间都不能实现派车辆运输的场景
    print("total_path_list", total_path_list)
    not_feasible_node_list = []
    feasible_node_list = []
    feasible_path_list = []
    for i in range(len(total_path_list)):
        path_list = total_path_list[i]
        arrive_time = demand_time_window[0, path_list[1]]
        leave_time = arrive_time + demand_service_time[path_list[1]]
        if path_list[1] in cannotbe_firstnode_served:
            not_feasible_node_list.extend(path_list[1:-1])
        else:
            # 默认第一个服务点的时间窗一定是满足要求的
            if len(path_list) == 3:
                # 返回配送中心的时间
                back_center_time = leave_time + travel_time_graph[path_list[-2]][0]
                if back_center_time > dis_center_open_time[1]:
                    not_feasible_node_list.append(path_list[-2])
                else:
                    # 只有一个配送节点的场景
                    feasible_node_list.append(path_list[-2])
            else:
                feasible_node_list.append(path_list[1])
                if len(path_list) == 4:
                    before_node = path_list[1]
                    cur_node = path_list[2]
                    arrive_time = leave_time + travel_time_graph[before_node][cur_node]
                    if (arrive_time < demand_time_window[0, cur_node]) or (arrive_time > demand_time_window[1, cur_node]):
                        # 不可行解
                        # 判断是否加入不可行解集合
                        if before_node in feasible_node_list:
                            feasible_node_list.remove(before_node)
                            not_feasible_node_list.append(before_node)
                            not_feasible_node_list.append(cur_node)
                        else:
                            not_feasible_node_list.append(cur_node)
                    else:
                        leave_time = arrive_time + demand_service_time[cur_node]
                        # 返回配送中心的时间
                        back_center_time = leave_time + travel_time_graph[cur_node][0]
                        if back_center_time > dis_center_open_time[1]:
                            # 判断是否加入不可行解集合
                            if before_node in feasible_node_list:
                                feasible_node_list.remove(before_node)
                                not_feasible_node_list.append(before_node)
                                not_feasible_node_list.append(cur_node)
                            else:
                                not_feasible_node_list.append(cur_node)
                        else:
                            # 判断是否加入可行解集合
                            if before_node in not_feasible_node_list:
                                not_feasible_node_list.append(cur_node)
                            else:
                                feasible_node_list.append(cur_node)
                else:
                    remain_node_list = path_list[2:-1]
                    for index in range(len(remain_node_list)):
                        cur_node = remain_node_list[index]
                        if len(remain_node_list) == 1:
                            before_node = remain_node_list[0]
                        else:
                            before_node = remain_node_list[index-1]
                        arrive_time = leave_time + travel_time_graph[before_node][cur_node]
                        if (arrive_time < demand_time_window[0, cur_node]) or (
                                arrive_time > demand_time_window[1, cur_node]):
                            # 不可行解
                            # 判断是否加入不可行解集合
                            if before_node in feasible_node_list:
                                feasible_node_list.remove(before_node)
                                not_feasible_node_list.append(before_node)
                                not_feasible_node_list.append(cur_node)
                            else:
                                not_feasible_node_list.append(cur_node)
                        else:
                            leave_time = arrive_time + demand_service_time[cur_node]
                            if cur_node == path_list[-2]:
                                # 返回配送中心的时间
                                back_center_time = leave_time + travel_time_graph[path_list[-2]][0]
                                if back_center_time > dis_center_open_time[1]:
                                    # 判断是否加入不可行解集合
                                    if before_node in feasible_node_list:
                                        feasible_node_list.remove(before_node)
                                        not_feasible_node_list.append(before_node)
                                        not_feasible_node_list.append(cur_node)
                                    else:
                                        # 判断是否加入可行解集合
                                        not_feasible_node_list.append(cur_node)
                                else:
                                    # 判断是否加入可行解集合
                                    if before_node in not_feasible_node_list:
                                        not_feasible_node_list.append(cur_node)
                                    else:
                                        feasible_node_list.append(cur_node)
                            else:
                                # 判断是否加入可行解集合
                                if before_node in not_feasible_node_list:
                                    not_feasible_node_list.append(cur_node)
                                else:
                                    feasible_node_list.append(cur_node)
    new_chrom = []
    if len(feasible_node_list) > 0:
        for node in feasible_node_list:
            new_chrom.append(node)
    if len(not_feasible_node_list) > 0:
        for node in not_feasible_node_list:
            new_chrom.append(node)
        not_feasible_node_flag = True
    else:
        not_feasible_node_flag = False
    print("new_chrom", new_chrom)
    return not_feasible_node_flag, feasible_node_list, new_chrom
def get_feasible_route(chrom):
    # 先判断是否满足车辆最大载重量限制
    cur_chrom = copy.deepcopy(chrom)
    not_feasible_node_flag = True
    count = 0
    while not_feasible_node_flag:
        # 先用得到满足车辆载重量的函数切出可行解路径
        print("*******000******")
        total_path_list = vehicle_capacity_restraint(cur_chrom)
        # 再使用时间窗判断是否路径也是满足时间窗要求的
        not_feasible_node_flag, feasible_node_list, new_chrom = time_window_restraint(total_path_list)
        print("not_feasible_node_flag", not_feasible_node_flag)
        if not_feasible_node_flag:
            print("*******001******")
            cur_chrom = new_chrom
            count += 1
        else:
            print("*******003******")
            return vehicle_capacity_restraint(new_chrom)
        if (count > 1) and (cur_chrom == new_chrom):
            return vehicle_capacity_restraint(new_chrom)  # 使用函数切出路线

算法迭代示意图

遗传算法迭代图如下:

在这里插入图片描述

连续两次运行程序,得到的目标值相同,下面图2比上图1在100代左右就寻找到了结果:

在这里插入图片描述

事不过三,连续三次,目标值开出来的都是478

在这里插入图片描述

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

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

相关文章

Pandas与数据库交互详解

Pandas 是一个强大的数据分析库&#xff0c;可以与各种数据库进行交互&#xff0c;从而可以方便地从数据库中读取数据、分析数据&#xff0c;并将结果写回数据库中。以下是使用 Pandas 与数据库交互的一般步骤&#xff1a; 一 、数据库交互 安装必要的库&#xff1a;首先&…

性能测试jmeter命令行运行+html测试报告解读

windows下打开jmeter的运行窗口&#xff0c;可以看到提示不要用GUI模式进行负载测试&#xff0c;如果要用负载测试&#xff0c;用cli模式&#xff0c;因为GUI模式运行jmeter比较消耗性能。 命令行模式 windows下找到jemeter所在文件夹&#xff0c;打开cmd输入命令。 jmeter -n…

【分享】7-Zip压缩包的密码可以取消吗?

7-Zip压缩包设置了“密码保护”&#xff0c;后面又不想要了&#xff0c;可以取消吗&#xff1f; 首先&#xff0c;我们要分两种情况来看&#xff0c;是记得密码&#xff0c;但不想每次打开压缩包都要输入密码&#xff0c;所以想取消密码&#xff0c;还是把密码忘记了所以想取消…

TDengine小知识-数据文件命名规则

TDengine 时序数据库对数据文件有自己的命名规则&#xff0c;文件名中包含了vnodeID、时间范围、版本、文件类型等多种信息。了解数据文件命名规则&#xff0c;可以让运维工作更简单。 废话不多说&#xff0c;直接上图&#xff1a; v4&#xff1a;文件所属 Vgroup 组&#xf…

基于epoll封装非阻塞的reactor框架(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、reactor架构二、client端reactor代码三、server端reactor代码四、单reactor架构可以实现百万并发总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项…

【MATLAB第79期】基于MATLAB的数据抽样合集(sobol、LHS拉丁超立方抽样、Halton、正交/均匀设计、随机rand函数)

【MATLAB第79期】基于MATLAB的数据抽样合集&#xff08;sobol、LHS拉丁超立方抽样、Halton、正交/均匀设计、随机rand函数&#xff09; 一、传统函数 1.指定区间随机生成数据&#xff08;小数&#xff09; [a b]区间随机数生成: Aa(b-a)rand(m,n) m&#xff1a;待生成矩阵A…

C语言实现用递归方法求 () = ∑ (^2)

完整代码&#xff1a; // 用递归方法求 ??(??) ∑ (??^2) #include<stdio.h>int func(int n){if (n1){return 1;}else{return n*nfunc(n-1);} }int main() {int n;printf("请输入一个整数");scanf("%d",&n);printf("%d",func(…

【C++】-还在玩普通的类吗,这里面有好几种特殊的类的设计,快进来看看

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

【代码随想录】算法训练营 第八天 第四章 字符串 Part 1

344. 反转字符串 题目 思路 我的思路是&#xff0c;用双指针&#xff0c;一个指左&#xff0c;一个指右&#xff0c;循环互换即可。 代码随想录的更简单精妙&#xff0c;直接用一个for循环搞定&#xff0c;里面用swap来互换。 代码 我的解法 class Solution { public:voi…

【IEEE】1区TOP仅1个月见刊(附IEEE旗下SCI实时影响因子汇总)

IEEE出版的SCI期刊有近200本&#xff0c;本期我们主要关注IEEE旗下被SCIE收录期刊的实时IF2023&#xff0c;所有期刊按照字母顺序排列。为方便对比&#xff0c;我们还给出了IF2022&#xff08;即今年6月公布的最新影响因子&#xff09;&#xff0c;供大家参考。 备注&#xff…

达索智能制造解决方案,敏捷电芯制造如何赋能企业竞争力 | 百世慧®

敏捷电芯制造赋能企业竞争力 全球电池市场正在快速扩大&#xff0c;为制造商带来巨大商机。 锂电行业的智能制造如何应用&#xff1f; 电池制造业的市场趋势是什么&#xff1f; 电池制造商面临哪些挑战&#xff1f; 特别是电池电芯制造方面&#xff0c;如何克服挑战获得竞…

阿里巴巴店铺所有商品数据接口及店铺商品数据分析

获取阿里巴巴店铺所有商品数据的接口是阿里巴巴开放平台提供的接口&#xff0c;通过该接口可以获取店铺所有商品数据。 通过阿里巴巴开放平台接口获取店铺所有商品数据的方法如下&#xff1a; 在开放平台注册成为开发者并创建一个应用&#xff0c;获取到所需的 App Key 和 Ap…

【华为认证超全科普帖】

华为认证是由华为基于“平台生态”战略&#xff0c;围绕“云-管-端”协同的新ICT技术架构&#xff0c;打造的业界覆盖ICT领域蕞广的认证体系。在信息和通信技术行业具有较高的含金量。 华为认证分为3个等级: HCIA (初级)&#xff1a;华为认证ICT工程师 HCIP (中级)&#xff1…

Pyside6 QMessageBox

Pyside6 QMessageBox QMessageBox使用5种基本消息自定义消息框QMessageBox标准按钮程序界面程序主程序 QMessageBox是一种通用的弹出式对话框&#xff0c;用于显示提示消息&#xff0c;允许用户点击不同的消息框按钮做出不同的判断。Pyside6提供了QMessageBox的操作函数&#x…

如何把Elasticsearch中的数据导出为CSV格式的文件

前言| 本文结合用户实际需求用按照数据量从小到大的提供三种方式从ES中将数据导出成CSV形式。本文将重点介Kibana/Elasticsearch高效导出的插件、工具集&#xff0c;通过本文你可以了解如下信息&#xff1a; 1&#xff0c;从kibana导出数据到csv文件 2&#xff0c;logstash导…

【EI会议征稿】第七届智能制造与自动化国际学术会议(IMA 2024)

第七届智能制造与自动化国际学术会议&#xff08;IMA 2024&#xff09; 2024 7th International Conference on Intelligent Manufacturing and Automation 第七届智能制造与自动化国际学术会议&#xff08;IMA 2024&#xff09;定于2024年1月12-14日在长沙隆重举行。会议主要…

自动注入@RequiredArgsConstructor

Autowired有波浪线&#xff0c;显示推荐使用构造器注入的方式。 但是以后需要自动注入的对象很多&#xff0c;写这么多构造函数代码会很长&#xff0c;可以在类上面加lombok中的RequiredArgsConstructor&#xff0c;表示必备参数的构造函数&#xff0c;给加final的成员变量生成…

使用Docker快速搭建Redis主从复制

目录 一、前言二、拉取Redis镜像三、创建挂载目录和添加配置文件3.1、主节点(6379)3.2、从节点(6380)3.3、从节点(6381) 四、启动Redis容器4.1、主节点(6379)4.2、从节点(6380)4.3、从节点(6381)4.4、启动命令参数介绍 五、查看各节点主从信息5.1、主节点(6379)5.2、从节点(638…