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

2023-10-20 17:44

本文主要是介绍如何写代码实现VRP问题中车辆容量限制及时间窗要求(python),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题研究背景

使用遗传模拟退火算法求解如下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 = Trueelse:not_feasible_node_flag = Falseprint("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 = Truecount = 0while 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_chromcount += 1else: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

在这里插入图片描述

这篇关于如何写代码实现VRP问题中车辆容量限制及时间窗要求(python)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/248669

相关文章

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo