【进阶六】Python实现SDVRPTW常见求解算法——遗传算法(GA)

基于python语言,采用经典遗传算法(GA)对 带硬时间窗的需求拆分车辆路径规划问题(SDVRP) 进行求解。

目录

  • 往期优质资源
  • 1. 适用场景
  • 2. 代码调整
    • 2.1 需求拆分
    • 2.2 需求拆分后的服务时长取值问题
  • 3. 求解结果
  • 4. 代码片段
  • 参考

往期优质资源


经过一年多的创作,目前已经成熟的代码列举如下,如有需求可私信联系,表明需要的 **问题与算法**,原创不宜,有偿获取。
VRP问题GAACOALNSDEDPSOQDPSOTSSA
CVRP
VRPTW
MDVRP
MDHVRP
MDHVRPTW
SDVRP
SDVRPTW

1. 适用场景

  • 求解SDVRPTW
  • 车辆类型单一
  • 车辆容量小于部分需求节点需求
  • 单一车辆基地
  • 带硬时间窗

2. 代码调整


2.1 需求拆分


与SDVRP问题相比,SDVRPTW问题不仅允许客户需求大于车辆载重,而且考虑了客户节点的时间窗约束。为了使得每个客户的需求得到满足,必须派遣一辆或多辆车辆在规定时间窗内对客户进行服务。对于需求节点的拆分,这里依然采取先验拆分策略,本文采用文献[1]提出的先验分割策略,表述如下:

(1)20/10/5/1拆分规则

  • m20 =max{ m ∈ Z + ∪ { 0 } ∣ 0.20 Q m < = D i m\in Z^+ \cup \{0\} | 0.20Qm <= D_i mZ+{0}∣0.20Qm<=Di }
  • m10 =max{ m ∈ Z + ∪ { 0 } ∣ 0.10 Q m < = D i − 0.20 Q m 20   m\in Z^+ \cup \{0\} | 0.10Qm <= D_i-0.20Qm_{20}~ mZ+{0}∣0.10Qm<=Di0.20Qm20  }
  • m5 =max{ m ∈ Z + ∪ { 0 } ∣ 0.05 Q m < = D i − 0.20 Q m 20 − 0.10 Q m 10 m\in Z^+ \cup \{0\} | 0.05Qm <= D_i-0.20Qm_{20}-0.10Qm_{10} mZ+{0}∣0.05Qm<=Di0.20Qm200.10Qm10 }
  • m1 =max{ m ∈ Z + ∪ { 0 } ∣ 0.01 Q m < = D i − 0.20 Q m 20 − 0.10 Q m 10 − 0.05 Q m 5 m\in Z^+ \cup \{0\} | 0.01Qm <= D_i-0.20Qm_{20}-0.10Qm_{10}-0.05Qm_{5} mZ+{0}∣0.01Qm<=Di0.20Qm200.10Qm100.05Qm5 }

(2)25/10/5/1拆分规则

  • m25 =max{ m ∈ Z + ∪ { 0 } ∣ 0.25 Q m < = D i m\in Z^+ \cup \{0\} | 0.25Qm <= D_i mZ+{0}∣0.25Qm<=Di }
  • m10 =max{ m ∈ Z + ∪ { 0 } ∣ 0.10 Q m < = D i − 0.25 Q m 25   m\in Z^+ \cup \{0\} | 0.10Qm <= D_i-0.25Qm_{25}~ mZ+{0}∣0.10Qm<=Di0.25Qm25  }
  • m5 =max{ m ∈ Z + ∪ { 0 } ∣ 0.05 Q m < = D i − 0.25 Q m 25 − 0.10 Q m 10 m\in Z^+ \cup \{0\} | 0.05Qm <= D_i-0.25Qm_{25}-0.10Qm_{10} mZ+{0}∣0.05Qm<=Di0.25Qm250.10Qm10 }
  • m1 =max{ m ∈ Z + ∪ { 0 } ∣ 0.01 Q m < = D i − 0.25 Q m 25 − 0.10 Q m 10 − 0.05 Q m 5 m\in Z^+ \cup \{0\} | 0.01Qm <= D_i-0.25Qm_{25}-0.10Qm_{10}-0.05Qm_{5} mZ+{0}∣0.01Qm<=Di0.25Qm250.10Qm100.05Qm5 }

在实现过程中,对于需求超过车辆容量的客户必须进行需求拆分,而对于未超过车辆容量的客户可以拆分也可以不拆分,这里设置了参数比例进行限制。

2.2 需求拆分后的服务时长取值问题


节点的服务时长会影响车辆的行进时间,进而会影响与节点时间窗的匹配问题。一般来说,节点的服务时长与需求量成正比关系,在进行节点需求拆分后,新节点的需求量降低,其服务时长理应也降低。但从标准数据集来看,各需求节点的服务时长均采用同一数值。因此本文在代码实现过程中也采用固定值,不考虑新节点服务时长的变化。当然,如有需要,也可以设置单位货物的服务时长,根据拆分后节点的具体需求量设置相应的服务时长。


3. 求解结果


(1)收敛曲线

在这里插入图片描述

(2)车辆路径
在这里插入图片描述
(3)输出内容

在这里插入图片描述


4. 代码片段


(1)数据结构

import math
import random
import numpy as np
import copy
import xlsxwriter
import matplotlib.pyplot as plt
import csv
import time
# 数据结构:解
class Sol():
    def __init__(self):
        self.obj=None # 目标函数值
        self.fitness = 0
        self.node_no_seq=[] # 解的编码
        self.route_list=[] # 解的解码
        self.timetable_list=[] # 车辆访问各点的时间
        self.route_distance_list = None
# 数据结构:需求节点
class Node():
    def __init__(self):
        self.id=0 # 节点id
        self.x_coord=0 # 节点平面横坐标
        self.y_coord=0  # 节点平面纵坐标
        self.demand=0 # 节点需求
        self.start_time=0 # 节点开始服务时间
        self.end_time=1440 # 节点结束服务时间
        self.service_time=0 # 单次服务时长
        self.vehicle_speed = 0 # 行驶速度
# 数据结构:车场节点
class Depot():
    def __init__(self):
        self.id=0 # 节点id
        self.x_coord=0 # 节点平面横坐标
        self.y_cooord=0  # 节点平面纵坐标
        self.start_time=0 # 节点开始服务时间
        self.end_time=1440 # 节点结束服务时间
        self.v_speed = 0 # 行驶速度
        self.v_cap = 80 # 车辆容量
# 数据结构:全局参数
class Model():
    def __init__(self):
        self.best_sol=None # 全局最优解
        self.sol_list=[] # 解的集合
        self.demand_dict = {}  # 需求节点集合
        self.depot = None  # 车场节点集合
        self.demand_id_list = [] # 需求节点id集合
        self.distance_matrix = {}  # 距离矩阵
        self.time_matrix = {}  # 时间矩阵
        self.number_of_demands = 0 # 需求点数量
        self.popsize=100 # 种群规模
        self.pc = 0.5  # 交叉概率
        self.pm = 0.2  # 变异概率
        self.n_select = 80  # 种群选择数量
        self.demand_id_list_ = []  # 经先验需求分割后的节点集合
        self.demand_dict_ = {}  # 需求分割后的节点需求集合
        self.distance_matrix_ = {}  # 原始节点id间的距离矩阵
        self.time_matrix_ = {}  # 原始节点id间的时间矩阵
        self.mapping = {}  # 需求分割前后的节点对应关系
        self.split_rate = 0.5 # 控制需求分割的比例(需求超出车辆容量的除外)

(2)距离矩阵

# 初始化参数:计算距离矩阵时间矩阵
def calDistanceTimeMatrix(model):
    for i in range(len(model.demand_id_list)):
        from_node_id = model.demand_id_list[i]
        for j in range(len(model.demand_id_list)):
            to_node_id = model.demand_id_list[j]
            dist = math.sqrt((model.demand_dict[from_node_id].x_coord - model.demand_dict[to_node_id].x_coord) ** 2
                             + (model.demand_dict[from_node_id].y_coord - model.demand_dict[to_node_id].y_coord) ** 2)
            model.distance_matrix[from_node_id, to_node_id] = dist
            model.time_matrix[from_node_id,to_node_id] = math.ceil(dist/model.depot.v_speed)
        dist = math.sqrt((model.demand_dict[from_node_id].x_coord - model.depot.x_coord) ** 2 +
                         (model.demand_dict[from_node_id].y_coord - model.depot.y_coord) ** 2)
        model.distance_matrix[from_node_id, model.depot.id] = dist
        model.distance_matrix[model.depot.id, from_node_id] = dist
        model.time_matrix[from_node_id,model.depot.id] = math.ceil(dist/model.depot.v_speed)
        model.time_matrix[model.depot.id,from_node_id] = math.ceil(dist/model.depot.v_speed)

(3)邻域搜索

# 计算适应度
def calFit(model):
    #calculate fit value:fit=Objmax-obj
    max_obj=-float('inf')
    best_sol=Sol()#record the local best solution
    best_sol.obj=float('inf')

    for sol in model.sol_list:
        node_no_seq=sol.node_no_seq
        sol.route_list= splitRoutes(node_no_seq, model)
        sol.timetable_list,sol.obj, sol.route_distance = calTravelCost(sol.route_list, model)
        if sol.obj > max_obj:
            max_obj = sol.obj
        if sol.obj < best_sol.obj:
            best_sol = copy.deepcopy(sol)
    #calculate fit value
    for sol in model.sol_list:
        sol.fitness = max_obj-sol.obj
    #update the global best solution
    if best_sol.obj<model.best_sol.obj:
        model.best_sol=best_sol
# 二元锦标赛
def selectSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    for _ in range(model.n_select):
        f1_index=random.randint(0,len(sol_list)-1)
        f2_index=random.randint(0,len(sol_list)-1)
        f1_fit=sol_list[f1_index].fitness
        f2_fit=sol_list[f2_index].fitness
        if f1_fit<f2_fit:
            model.sol_list.append(sol_list[f2_index])
        else:
            model.sol_list.append(sol_list[f1_index])
# OX交叉
def crossSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        [f1_index,f2_index] = random.sample(range(len(sol_list)),2)
        f1 = copy.deepcopy(sol_list[f1_index])
        f2 = copy.deepcopy(sol_list[f2_index])
        if random.random() <= model.pc:
            cro1_index = random.randint(0,model.number_of_demands-1)
            cro2_index = random.randint(cro1_index,model.number_of_demands-1)
            new_c1_f = []
            new_c1_m=f1.node_no_seq[cro1_index:cro2_index+1]
            new_c1_b = []
            new_c2_f = []
            new_c2_m=f2.node_no_seq[cro1_index:cro2_index+1]
            new_c2_b = []
            for index in range(model.number_of_demands):
                if len(new_c1_f)<cro1_index:
                    if f2.node_no_seq[index] not in new_c1_m:
                        new_c1_f.append(f2.node_no_seq[index])
                else:
                    if f2.node_no_seq[index] not in new_c1_m:
                        new_c1_b.append(f2.node_no_seq[index])
            for index in range(model.number_of_demands):
                if len(new_c2_f)<cro1_index:
                    if f1.node_no_seq[index] not in new_c2_m:
                        new_c2_f.append(f1.node_no_seq[index])
                else:
                    if f1.node_no_seq[index] not in new_c2_m:
                        new_c2_b.append(f1.node_no_seq[index])
            new_c1=copy.deepcopy(new_c1_f)
            new_c1.extend(new_c1_m)
            new_c1.extend(new_c1_b)
            f1.node_no_seq=new_c1
            new_c2=copy.deepcopy(new_c2_f)
            new_c2.extend(new_c2_m)
            new_c2.extend(new_c2_b)
            f2.node_no_seq=new_c2
            model.sol_list.append(copy.deepcopy(f1))
            model.sol_list.append(copy.deepcopy(f2))
        else:
            model.sol_list.append(copy.deepcopy(f1))
            model.sol_list.append(copy.deepcopy(f2))
        if len(model.sol_list)>model.popsize:
            break
# 变异
def muSol(model):
    sol_list=copy.deepcopy(model.sol_list)
    model.sol_list=[]
    while True:
        f1_index = random.randint(0, len(sol_list) - 1)
        f1 = copy.deepcopy(sol_list[f1_index])
        m1_index=random.randint(0,model.number_of_demands-1)
        m2_index=random.randint(0,model.number_of_demands-1)
        if m1_index!=m2_index:
            if random.random() <= model.pm:
                node1=f1.node_no_seq[m1_index]
                f1.node_no_seq[m1_index]=f1.node_no_seq[m2_index]
                f1.node_no_seq[m2_index]=node1
            model.sol_list.append(copy.deepcopy(f1))
            if len(model.sol_list)>model.popsize:
                break

参考

【1】 A novel approach to solve the split delivery vehicle routing problem

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【哈希表专题】(1. 两数之和 面试题 01.02. 判定是否互为字符重排 217. 存在重复元素 219. 存在重复元素 II 49. 字母异位词分组)

文章目录 哈希表1. 两数之和面试题 01.02. 判定是否互为字符重排217. 存在重复元素219. 存在重复元素 II49. 字母异位词分组 哈希表 哈希表是什么&#xff1a;存储数据的容器 作用&#xff1a;快速查找某个元素。O(1) 当我们需要频繁的查找某一个数的时候&#xff0c;可以使…

YOLO火灾烟雾检测数据集:20000多张,yolo标注完整

YOLO火灾烟雾检测数据集&#xff1a;一共20859张图像&#xff0c;yolo标注完整&#xff0c;部分图像应用增强 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或其他任何数据集请私信

蓝桥杯备考2

P8839 [传智杯 #4 初赛] 组原成绩 题目描述 花栗鼠科技大学&#xff08;Hualishu University of Science and Technology, HUST&#xff09;的计算机组成原理快要出分了。你现在需要计算你的组原成绩如何构成。 具体来说&#xff0c;组原成绩分为三部分&#xff0c;分别是平…

算法学习系列(四十六):迭代加深、双向DFS

目录 引言概念一、加成序列二、送礼物 引言 本文主要讲了&#xff0c; D F S DFS DFS 的另外两种优化&#xff0c;分别是迭代加深和双向 D F S DFS DFS &#xff0c;思路还是非常清晰明了的&#xff0c;只要会写 D F S DFS DFS 那么这些剪枝和优化其实还是非常的容易的&…

多线程学习-线程安全

目录 1.多线程可能会造成的安全问题 2. static共享变量 3.同步代码块 4.同步方法 5.使用Lock手动加锁和解锁 6.死锁 1.多线程可能会造成的安全问题 场景&#xff1a;三个窗口同时售卖100张电影票&#xff0c;使用线程模拟。 public class MyThread extends Thread{//tic…

AI结合机器人的入门级仿真环境有哪些?

由于使用真实的机器人开发和测试应用程序既昂贵又费时&#xff0c;因此仿真已成为机器人应用程序开发中越来越重要的部分。在部署到机器人之前在仿真中验证应用程序可以通过尽早发现潜在问题来缩短迭代时间。通过模拟&#xff0c;还可以更轻松地测试在现实世界中可能过于危险的…

Linux文件搜索工具(gnome-search-tool)

opensuse下安装: sudo zypper install gnome-search-tool 操作界面:

【剑指offr--C/C++】JZ59 滑动窗口的最大值

一、题目 二、思路及代码 暴力解法是依次往后滑动一位&#xff0c;然后比较窗口内的值。 我这里考虑&#xff1a;窗口每次往后移动一位&#xff0c;那么如果当前窗口的最大值max在窗口内部&#xff0c;那么再滑动到下一个窗口的时候&#xff0c;窗口内只有最新进来的一个元素没…

解决:CloudCompare中display选择Full screen后无法恢复且无法关闭

问题 在CloudCompare中display选择Full screen进行全屏显示时&#xff0c;软件各按钮失效且软件无法关闭 解决 按下F9键退出全屏模式&#xff0c;笔记本电脑可能需要FnF9同时按下。

算法 - 符号表-下

&#x1f600;前言 推荐从上看到下 算法 - 符号表-上 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 算法 - 符号表查找树1. 插入操作2. 性质 红黑树1. 左旋转2. 右旋转3. 颜色转换4. 插入5. 分析 散列表1. 散列函数2. 拉链法3. 线性探测法3.1 查找3.2 插入3.3 删除3.5 …

中兴天机A31 A31PRO 5G zte A2122H te A2022H 解锁BootLoader root权限 教程magisk,原厂刷机包

zte A2122H P768A02 zte A2022H P875A02 中兴天机A31 A31PRO 5G zte A2122H te A2022H 解锁BootLoader root教程magisk&#xff0c;原厂刷机包 感谢 某大神支持&#xff0c;已经解锁root 刷了面具&#xff1b; 中兴天机A31 A31PRO 5G zte A2122H te A2022H 解锁BootLoad…

Oracle 数据库中的全文搜索

Oracle 数据库中的全文搜索 0. 引言1. 整体流程2. 创建索引2-1. 创建一个简单的表2-2. 创建文本索引2-3. 查看创建的基础表 3. 运行查询3-1. 运行文本查询3-2. CONTAINS 运算符3-3. 混合查询3-4. OR 查询3-5. 通配符3-6. 短语搜索3-7. 模糊搜索&#xff08;Fuzzy searches&…

Francek Chen 的128天创作纪念日

目录 Francek Chen 的128天创作纪念日机缘收获日常成就憧憬 Francek Chen 的128天创作纪念日 Francek Chen 的个人主页 机缘 不知不觉的加入CSDN已有两年时间了&#xff0c;最初我第一次接触CSDN技术社区是在2022年4月的时候&#xff0c;通过学长给我们推荐了几个IT社区平台&a…

代码随想录-力扣刷题-总结笔记02

代码随想录&#xff1a;代码随想录力扣&#xff1a;力扣 (LeetCode) 全球极客挚爱的技术成长平台 代码随想录-力扣刷题-总结笔记01代码随想录-力扣刷题-总结笔记02 目录 01、代码随想录 00、其他 ArrayList转数组 07、二叉树 7.0、递归法 7.1、二叉树的层序遍历模板 7.2…

Lan仿朋友圈系统开源源码 表白墙 打造朋友圈工具 仿朋友圈界面 朋友圈模拟器在线

(购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不要购买!购买本专栏住如有什么源码需要,可向博主私信,第二天即可发布!博主有几万资源) Lan仿朋友圈系统开源,可用于表白墙等…

STL中各类容器详细介绍

STL介绍 STL&#xff08;Standard Template Library&#xff09;&#xff0c;即标准模板库&#xff0c;是一个具有工业强度的&#xff0c;高效的C程序库。它被容纳于C标准程序库&#xff08;C Standard Library&#xff09;中&#xff0c;是ANSI/ISO C标准中最新的也是极具革命…

tsv、csv、xls等文件类型区别及处理(python版)

目录 前言 介绍 tsv、csv、txt的区别 读取/生成 不同格式数据文件&#xff08;python&#xff09; 一、读取/生成csv数据文件 二、读取/生成txt数据文件 三、读取/生成tsv数据文件 四、读取/生成xls数据文件 不同文件格式转化 总结 前言 考虑到进行机器学习、深度学习…

代码随想录day35--动态规划的应用2||01背包理论基础、携带研究材料

01背包理论基础 有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值为 value[i]。每件物品只能用一次&#xff0c;将这些物品装入背包里物品价值总和最大。 这是很标准的背包问题&#xff0c;很多同学看到后很自然的就想到了背包&#xff0c;我们…

【Linux学习】Linux 的虚拟化和容器化技术

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

少儿编程 2024年3月电子学会图形化编程等级考试Scratch一级真题解析(选择题)

2024年3月scratch编程等级考试一级真题 选择题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 1、单击下列哪个按钮&#xff0c;能够让舞台变为“全屏模式” A、 B、 C、 D、 答案&#xff1a;C 考点分析&#xff1a;考查scratch平台的使用&…