【进阶五】Python实现SDVRP(需求拆分)常见求解算法——自适应大邻域算法(ALNS)

自适应大领域搜索算法(ALNS) 详解及python示例 1 前言 TSP问题的求解方法层出不穷,遗传算法、模拟退火、粒子群算法之类的介绍已经比较多了,同时发现关于自适应大领域搜索算法(Adaptive Large Neighborhood Search, ALNS)的介绍比较少,而且示例代码注释不太详细,刚接触的时候学起来有点费劲,就把学习资料和自己理解的过程写一下,造福和我一样的菜鸟吧。 2 什么是自适应大领域搜索 概念理解:干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇 优点、步 阅读详情

基于python语言,采用经典自适应大邻域算法(ALNS)对 需求拆分车辆路径规划问题(SDVRP) 进行求解。

往期优质资源


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

1. 适用场景

  • 求解CVRP
  • 车辆类型单一
  • 车辆容量小于部分需求节点需求
  • 单一车辆基地

2. 代码调整


与CVRP问题相比,SDVRP问题允许客户需求大于车辆容量。为了使得每个客户的需求得到满足,必须派遣一辆或多辆车辆对客户进行服务,也就是需要对客户的需求进行拆分。关于如何进行拆分一般有两种方式:

  • 先验拆分策略:提前制定策略对客户的需求(尤其是大于车辆容量的客户需求)进行分解,将SDVRP问题转化为CVRP问题
  • 过程拆分策略:在车辆服务过程中对客户需求进行动态拆分

本文采用文献[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 m∈Z+∪{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}~ m∈Z+∪{0}∣0.10Qm<=Di​−0.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} m∈Z+∪{0}∣0.05Qm<=Di​−0.20Qm20​−0.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} m∈Z+∪{0}∣0.01Qm<=Di​−0.20Qm20​−0.10Qm10​−0.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 m∈Z+∪{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}~ m∈Z+∪{0}∣0.10Qm<=Di​−0.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} m∈Z+∪{0}∣0.05Qm<=Di​−0.25Qm25​−0.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} m∈Z+∪{0}∣0.01Qm<=Di​−0.25Qm25​−0.10Qm10​−0.05Qm5​ }

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

3. 求解结果


(1)收敛曲线
在这里插入图片描述

(2)车辆路径

在这里插入图片描述

4. 代码片段


(1)数据结构

# 数据结构:解
class Sol():
    def __init__(self):
        self.node_no_seq = None # 节点id有序排列
        self.obj = None # 目标函数
        self.fitness = None  # 适应度
        self.route_list = None # 车辆路径集合
        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 # 节点需求
# 数据结构:全局参数
class Model():
    def __init__(self):
        self.best_sol = None # 全局最优解
        self.demand_id_list = [] # 需求节点集合
        self.demand_dict = {}
        self.sol_list = [] # 解的集合
        self.depot = None # 车场节点
        self.number_of_demands = 0 # 需求节点数量
        self.vehicle_cap = 0 # 车辆最大容量
        self.distance_matrix = {} # 节点距离矩阵
        self.demand_id_list_ = [] # 经先验需求分割后的节点集合
        self.demand_dict_ = {} # 需求分割后的节点需求集合
        self.distance_matrix_ = {}  # 原始节点id间的距离矩阵
        self.mapping = {}  # 需求分割前后的节点对应关系
        self.split_rate = 0.5 # 控制需求分割的比例(需求超出车辆容量的除外)
        self.popsize = 100 # 种群规模

        self.rand_d_max = 0.4  # 随机破坏最大破坏比例
        self.rand_d_min = 0.1  # 随机破坏最小破坏比例
        self.worst_d_min = 5  # 最坏值破坏最少破坏数量
        self.worst_d_max = 20  # 最坏值破坏最多破坏数量
        self.regret_n = 5  # 后悔值破坏数量
        self.r1 = 30  # 一等得分值
        self.r2 = 18  # 二等得分值
        self.r3 = 12  # 三等得分值
        self.rho = 0.6  # 权重衰减比例
        self.d_weight = np.ones(2) * 10  # 破坏算子权重
        self.d_select = np.zeros(2)  # 破坏算子选择次数
        self.d_score = np.zeros(2)  # 破坏算子得分
        self.d_history_select = np.zeros(2)  # 破坏算子累计选择次数
        self.d_history_score = np.zeros(2)  # 破坏算子累计得分
        self.r_weight = np.ones(3) * 10  # 修复算子权重
        self.r_select = np.zeros(3)  # 修复算子选择次数
        self.r_score = np.zeros(3)  # 修复算子得分
        self.r_history_select = np.zeros(3)  # 修复算子累计选择次数
        self.r_history_score = np.zeros(3)  # 修复算子累计得分

(2)距离矩阵

# 初始化参数
def cal_distance_matrix(model):
    for i in model.demand_id_list:
        for j in model.demand_id_list:
            d=math.sqrt((model.demand_dict[i].x_coord-model.demand_dict[j].x_coord)**2+
                        (model.demand_dict[i].y_coord-model.demand_dict[j].y_coord)**2)
            model.distance_matrix[i,j]=max(d,0.0001) if i != j else d
        dist = math.sqrt((model.demand_dict[i].x_coord - model.depot.x_coord) ** 2 + (model.demand_dict[i].y_coord - model.depot.y_coord) ** 2)
        model.distance_matrix[i, model.depot.id] = dist
        model.distance_matrix[model.depot.id, i] = dist

(3)破坏算子

# 随机破坏
def createRandomDestory(model):
    d=random.uniform(model.rand_d_min,model.rand_d_max)
    return random.sample(model.demand_id_list_,int(d*(len(model.demand_id_list_)-1)))
# 最坏值破坏
def createWorseDestory(model,sol):
    deta_f=[]
    for node_no in sol.node_no_seq:
        node_no_seq_=copy.deepcopy(sol.node_no_seq)
        node_no_seq_.remove(node_no)
        obj,_,_=calObj(node_no_seq_,model)
        deta_f.append(sol.obj-obj)
    sorted_id = sorted(range(len(deta_f)), key=lambda k: deta_f[k], reverse=True)
    d=random.randint(model.worst_d_min,model.worst_d_max)
    return [sol.node_no_seq[i] for i in sorted_id[:d]]

(4)修复算子

# 随机修复
def createRandomRepair(remove_list,model,sol):
    unassigned_node_no_seq = remove_list
    assigned_node_no_seq = [node_no for node_no in sol.node_no_seq if node_no not in remove_list]
    # insert
    for node_no in unassigned_node_no_seq:
        index=random.randint(0,len(assigned_node_no_seq)-1)
        assigned_node_no_seq.insert(index,node_no)
    new_sol=Sol()
    new_sol.node_no_seq=copy.deepcopy(assigned_node_no_seq)
    new_sol.obj,new_sol.route_list,new_sol.route_distance=calObj(assigned_node_no_seq,model)
    return new_sol
# 贪婪修复
def createGreedyRepair(remove_list,model,sol):
    unassigned_node_no_seq = remove_list
    assigned_node_no_seq = [node_no for node_no in sol.node_no_seq if node_no not in remove_list]
    #insert
    while len(unassigned_node_no_seq)>0:
        insert_node_no,insert_index=findGreedyInsert(unassigned_node_no_seq,assigned_node_no_seq,model)
        assigned_node_no_seq.insert(insert_index,insert_node_no)
        unassigned_node_no_seq.remove(insert_node_no)
    new_sol=Sol()
    new_sol.node_no_seq=copy.deepcopy(assigned_node_no_seq)
    new_sol.obj,new_sol.route_list,new_sol.route_distance=calObj(assigned_node_no_seq,model)
    return new_sol

参考

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

代码 | 自适应大邻域搜索系列之(5) - ALNS_Iteration_Status和ALNS_Parameters的代码解析... 代码 | 自适应大邻域搜索系列之(5) - ALNS_Iteration_Status和ALNS_Parameters的代码解析 前言 上一篇推文说了,后面的代码难度直线下降,各位小伙伴可以放去n的100次方心了。今天讲讲一些细枝末节,就是前面一直有提到的参数和一些状态的记录代码。这个简单啦,小编也不作过多解释了。大家直接看代码都能看懂,不过小编还... 阅读详情

相关推荐

Intel SGX Explained

计算机中最主要的资源是CPU和内存,由操作系统和虚拟管理系统等系统软件来管理这些资源。

hanqdi_的博客 3456

10分钟彻底理解自适应大邻域搜索算法

阅读本文大概需要 10分钟。算法介绍自适应大邻域搜索算法(Adaptive Large Neighborhood Search),简称(ALNS),是由Ropke与Pisinger在20...

木川的技术之路 6242

【鸿蒙游戏上架审核规范指南】

华为鸿蒙游戏上架审核规范指南摘要

weixin_44161102的博客 924

蚁群算法求解需求可拆分车辆路径问题(SDVRP)

今天给大家介绍SDVRP(split delivery vehicle routing problem,需求可拆分的车辆路径问题),同时介绍蚁群优化算法求解SDVRP。话不多说,我们直接开始。传统的VRP通常假设车辆载重大于客户需求。如果车辆载重小于顾客的实际需求,就需要多辆车来服务一个顾客,由此衍生出了SDVRP。SDVRP,即需求可拆分的车辆路径问题,是车辆路径问题的变体,它在VRP的基础之上增加了一个条件:每个顾客的需求可以拆分并分配给不同的车辆配送,而不是必须由一个车辆在一条路径中满足。

eternal1995的博客 2666

架构决策之消息中间件MQ系列六-Pulsar

一、架构

恰恰虎的博客 7253

学Simulink——基于Simulink的固态变压器(SST)多级协同控制​

本文介绍了基于Simulink的固态变压器(SST)多级协同控制系统设计与仿真方法。SST作为智能电网关键设备,包含AC-DC整流、DC-DC隔离和DC-AC逆变三级变换器。针对级间耦合问题,提出了能量-功率协同控制策略,通过纵向解耦和横向联动实现稳定运行。详细演示了在Simulink中搭建三级主电路、设计协同控制器(含电网定向控制、功率前馈和虚拟惯性控制)的全过程,并通过负载突变测试验证了系统动态性能(直流母线电压波动<2%)。最后给出了工程部署建议,包括数字控制延时补偿、启动序列设计和代码自动生成

xiaoheshang_123的博客 206

【改进篇】Python实现VRP常见求解算法——自适应大邻域算法(ALNS)

基于python语言,实现经典自适应大邻域算法(ALNS)对车辆路径规划问题(CVRP)进行求解, 优化代码结构,改进Split函数

Better.C的博客 2717

调节桌面图标大小的方法(编程)

如果你对其他操作系统或编程语言有类似的需求,可以根据具体的情况进行相应的调整和搜索相关的库和方法。在编程中,我们可以使用各种桌面应用程序来调节桌面图标的大小。库提供的函数,我们获取了桌面图标的窗口句柄,并使用Windows消息发送方式设置图标的大小。你可以根据自己的需求修改代码中的图标大小值,并在运行代码后观察桌面图标的变化。函数并传入所需的图标大小,这里设置为48x48像素。首先,我们需要使用Python的第三现的方法。首先,我们需要使用Python的第三方库。首先,我们需要使用实现的方法。

CyberFlare的博客 439

【进阶六】Python实现SDVRPTW常见求解算法——自适应大邻域算法(ALNS)

基于python语言,采用经典自适应大邻域算法(ALNS)对 带硬时间窗的需求拆分车辆路径规划问题(SDVRPTW) 进行求解。

Better.C的博客 1718

QMK键盘固件开发零基础教程(2025全新重制版)

《QMK键盘固件开发零基础教程(2025全新重制版)》是一套专为键盘爱好者和开发者设计的全面教程,旨在帮助用户从零基础快速掌握最新的QMK固件开发技术。教程基于2025年最新的QMK版本(0.19.8+),解决了旧版教程中的诸多问题,如概念不清、步骤缺失和代码过时等。新版教程提供了详细的图解、完整的项目源码以及从环境配置到高级功能开发的全流程指导。此外,教程还涵盖了2024年QMK生态的重大变革,包括键码系统的重构、编译工具链的更新以及Config文件格式的改变等。付费版教程额外提供独家内容,如新特性解析、

Despacito0o的博客 1121

在 SAP SD 模块里,销售订单号既可以由系统“自动”产生,也可以由用户“手工”输入,核心取决于“号码段(Number Range)”与“订单类型(Sales Document Type)”这两处配

SAP销售订单编号配置指南:系统提供内部自动编号和外部手工编号两种方式,通过VN01定义号码段(内部/外部),VOV8分配给订单类型并设置"ExtNo.Rq"选项即可切换。高阶应用支持灵活编号(如加前缀)或通过增强出口完全自定义编号规则。配置时需注意号码段创建、分配及传输问题,常见故障包括号码范围不存在或配置错误。核心是"号码段+订单类型"的组合控制编号生成方式,满足从简单到复杂的业务需求。

金牌架构师 531

智能优化系列|第一期:自适应大领域搜索算法(ALNS算法)详解与实战案例(附python代码)

​ Adaptive Large Neighborhood Search (ALNS) 算法是一种元启发式算法,用于解决组合优化问题。ALNS 算法结合了离散优化中的邻域搜索和元启发式算法的特点,能够有效地应对各种复杂的优化问题。 ALNS 算法的核心思想自适应大领域搜索的核心思想是:破坏解、修复解、动态调整权重并选择(自适应)。通过设计多组破坏算子和修复算子,扩大解空间搜索范围,对当前解进行改进,表现好的破坏和修复方法相应地得到高分,权重也越高。在每次迭代中,根据以往表现对各个破坏和修复算子进行选择..

扎实耕耘,终有所获 5681

MOS管驱动电路设计全攻略

MOS管驱动电路设计全攻略

让你爱上电路设计 3638

AdaBoost:自适应提升算法(Numpy手写代码实战)

AdaBoost:自适应提升算法(Numpy手写代码实战)

wzk4869的博客 1112

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Python实现VRP常见求解算法——自适应大邻域搜索算法(ALNS)

基于python语言,实现经典自适应大邻域搜索算法(ALNS)对车辆路径规划问题(CVRP)进行求解。

Better.C的博客 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

自适应大规模邻域算法(ALNS)解决VRPTW问题(JAVA)

本文介绍了一种启发式算法(ALNS)的Java代码,并用该算法求解了VRPTW问题实例(SolomnC101(100)算例)。结果显示算法的运行时间在可接受范围内。

Made-In-China-0711的博客 8660

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

ALNS算法终极指南:Python自适应大邻域搜索完整教程

ALNS(自适应大邻域搜索)是一个强大的Python优化算法库,专门用于解决复杂的组合优化问题。无论你是算法工程师、运筹学研究者,还是数据科学爱好者,这个开源工具都能帮助你在庞大的解空间中快速找到高质量近似解。 ## 🎯 什么是自适应大邻域搜索? ALNS算法的核心思想很巧妙:通过动态调整破坏和修复操作的组合,在搜索过程中不断优化解的质量。相比于传统启发式算法,ALNS具有更强的适应性和更高

gitblog_00258的博客 1252

探索优化之旅:深入解析Python中的ALNS框架

探索优化之旅:深入解析Python中的ALNS框架 项目地址:https://gitcode.com/gh_mirrors/al/ALNS 项目介绍 在当今这个数据驱动的时代,解决复杂的组合优化问题成为众多行业的需求关键。ALNS——一款基于Python实现的自适应大邻域搜索(Adaptive Large Neighbourhood Search)框架,正是一把解锁这些问题的强大工具。它提供了一种...

gitblog_00040的博客 853

自适应大领域搜索算法

概念理解干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇优点、步骤和python示例代码自适应大邻域搜索算法参考文献及算法应用[1]王新. 车辆和无人机联合配送路径问题研究[D].大连海事大学,2020.[2]李婷玉. 多商户多车程同城物流配送车辆调度问题研究[D].大连理工大学,2018.[3]张梦颖. 不确定因素下路径规划问题研究[D].中国科学技术大学,2016.破坏解修复解动态调整权重并选择(自适应)。

再来一下! 1735

运筹系列39:ALNS使用

1. ALNS介绍 ALNS(Adaptive Large Neighborhood Search)是现在用的很多的启发式算法,发表于2010年。其基本思路是不断destroying和repairing问题。 定义XXX为问题III的可行解集,c(X)表示要优化的目标。我们从一个初始解x1出发,搜索N(x1)领域范围内的最优值x2,然后再搜索N(x2)范围内的最优值x3……,这是最优梯度下降法。 ...

kittyzc的博客 8178

自适应大邻域搜索(Adaptive Large Neighborhood Search)(二)解决TSP问题

https://mp.weixin.qq.com/s/l7fTHRye4pPBtWrlPrZ6mA 1. ALNS介绍 从LNS谈起 LNS,即大规模邻域搜索算法(large neighborhood search)由Shaw在论文A new local search algorithm providing high quality solutions to vehicle routing problems中提出。主要的算法分为两部分:remove and insert,每次从当前的解中移除一定量的顾客(在

qq_36744449的博客 3491

[大邻域算法](MD)VRPTW常见求解算法--代码解析

文章代码的解析篇

panbaoran913的博客 2687

运筹系列63:使用ALNS求解大规模TSP问题

参见这篇访谈:https://www.oberlin.edu/bob-bosch 文章见:https://www2.oberlin.edu/math/faculty/bosch/tspart-page.html 1. TSP ART概述 将灰度图片先转为离散图,然后将离散图用一笔画画出来 转为离散点的方法,使用的是weighted Voronoi stippling方法,参考这篇论文:https://www.cs.ubc.ca/labs/imager/tr/2002/secord2002b/secord.

kittyzc的博客 2032

5.Python实现VRP常见求解算法——自适应大邻域搜索算法(ALNS)

参考笔记 https://github.com/PariseC/Algorithms_for_solving_VRP 1. 适用场景 求解CVRP 车辆类型单一 车辆容量不小于需求节点最大需求 单一车辆基地 2. 问题分析 CVRP问题的解为一组满足需求节点需求的多个车辆的路径集合。 假设某物理网络中共有10个顾客节点,编号为1~10,一个车辆基地,编号为0,在满足车辆容量约束与顾客节点需求约束的条件下,此问题的一个可行解可表示为:[0-1-2-0,0-3-4-5-0,0-6-7-8-0,0-9-10-

qq_36744449的博客 3716

自适应大领域搜索算法(ALNS)

以下是一些常见的调试思路和策略:

weixin_55061195的博客 1650

ALNS求MDHVRPTW问题 python实现

ALNS python实现 VRP MDHVRPTW 自适应大邻域搜索

anthea_luo的博客 1775
上一篇: 【进阶五】Python实现SDVRP(需求拆分)常见求解算法——蚁群算法(ACO)
下一篇: 【进阶五】Python实现SDVRP(需求拆分)常见求解算法——差分进化算法(DE)
Better.C
博客等级 码龄9年 1万+粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Better.C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值