基于python编码实现多智能体进化算法求解带硬时间窗约束的VRP问题(适配版)

作者:Logintern09
发布时间:2022年10月23日16时
出处:CSDN博客
专栏:《智能优化算法》

书接上回:多智能体进化算法求解带硬时间窗约束的VRP问题(附完整python程序代码+思路详解)。以上一版本的硬时间窗VRP多智能体进化算法为基础版本,进行新增需求程序的迭代工作。

增加车辆数量约束限制

需求来源

我在文章:《多智能体进化算法求解带时间窗的VRP问题(python)》中最后的结论分析部分就参考文献《带时间窗VRP问题的多智能体进化算法》与我编码算法求解得到的最优解不一致问题做了说明,原因是编码时没有考虑到文献数学建模时提到的车辆数量不能超过3辆的约束限制。

在这里插入图片描述

用于求解单配送中心带硬时间窗约束VRP的多智能体进化算法这一版程序的基础上添加“车辆数量不能超过3辆”的约束条件。

修改思路

在不大动基础版本程序结构的前提下考虑,如何以最小的代码达到新增需求的修改。

我们先来回顾一下VRP问题中智能体整数编码的要求:在编码过程中没有包括配送中心(配送中心用编号0表示),而对载质量约束和时间窗约束进行检验时需要将配送中心涵盖在上述智能体编码中以便检验。比如随机产生的智能体为1-4-5-7-9-6-3-8-2,则从左到右累加计算各个需求点的载质量,倘若载质量超过配送车辆最大载质量,则在前一客户需求点添加0,然后将累加后的载质量重置为0,再依次进行累加。比如添置后的结果为0-1-4-5-0-7-9-0-6-3-8-2-0,表示在此配送过程中需要3辆配送车辆,对应的路径分别为0→1→4→5→0、0→7→9→0、0→6→3→8→2→0。

从上述可行解的编码要求可以看出:在构造可行解的过程中如果需要第三次插入配送中心节点0,说明需要产生第四条新的运输路径,配送车辆的数量超过了3辆,此时可认为该智能体编码是非可行解。

基于上述讨论,基础版本程序的修改思路如下:在构造可行解的过程中如果需要第三次插入配送中心则认为该智能体编码是非可行解,跳出解的构造过程重新生成新的智能体,保证所有生成的智能体都是满足“车辆数量不超过3辆”这一约束条件的。

代码实现

代码实现部分要考虑程序迭代版本的可维护性。

(1)在算法输入参数文件input_params.py文件的Data_class类中增加车辆数量限制的开关变量switch_vehicle_num作为可控变量,目的是使这版程序同时适配车辆数量不受限制和车辆数量受限两种情况,同时增加适应于验证算例的车辆数量限制条件,即vehicle_num = 3;

(2)总览整个算法程序,需要在以下三处地方增加车辆数量限制。

第一处是生成初始智能体网络。函数gen_init_solution()中判断随机生成的编码是否为可行解的检验函数get_feasible_solution()中应该增加车辆数量限制条件。

看下函数get_feasible_solution()的代码片段:

def get_feasible_solution(solution_seed):
    quantity_list = []
    arrive_time_list, leave_time_list = [0], [0]
    new_solution_seed = [0]
    demand_quantity = Data_class.demand_quantity
    vehicle_capacity_max = Data_class.vehicle_capacity_max
    travel_time_graph = Data_class.travel_time_graph
    demand_time_window = Data_class.demand_time_window
    demand_service_time = Data_class.demand_service_time
    feasible_start_nodes = Data_class.feasible_start_nodes
    init_solution_seed = deepcopy(solution_seed)
    for i in range(len(init_solution_seed)):
        quantity_list.append(demand_quantity[0, init_solution_seed[i]])
        travel_time = travel_time_graph[new_solution_seed[-1], init_solution_seed[i]]
        # 离开上一个节点的时间+上一个节点到当前节点的行驶时间=到达当前节点的时间
        arrive_curnode_time = leave_time_list[-1] + travel_time
        arrive_time_list.append(arrive_curnode_time)
        # 先判断是否超过车辆的最大载重量限制,再判断是否满足时间窗要求
        if np.sum(np.array(quantity_list)) > vehicle_capacity_max:
            # 超过车辆的最大载重量限制
            if init_solution_seed[i] in feasible_start_nodes:
                # 插入配送中心
                if i != 0:
                    new_solution_seed.append(0)
                    # 考虑车辆数量限制
                    switch_vehicle_num = Data_class.switch_vehicle_num
                    if switch_vehicle_num == 1:
                        count = 0
                        vehicle_num = Data_class.vehicle_num
                        for node in new_solution_seed:
                            if node == 0:
                                count += 1
                        if count > vehicle_num:
                            # 不满足车辆数量限制,可以确定该组随机产生的初始解是不可行解
                            return False, []

主要修改点是在插入配送中心后,判断此时解的编码new_solution_seed中包含配送中心节点“0”的个数,如果此时配送中心的个数大于车辆数量,则认为生成的初始编码init_solution_seed是非可行解,可直接退出函数。

第二处是智能体的竞争。函数compete_operation()代码如下:

def compete_operation(compete_solution):
    demand_node_num = Data_class.demand_node_num
    choice_idx_list = []
    origin_solution_seed, pending_code_segment = [], []
    # 直接截取从配送中心开始的一段编码
    for idx, node in enumerate(compete_solution):
        if node == 0:
            choice_idx_list.append(idx)
    while 1:
        rand_idx = random.choice(choice_idx_list[:-1])  # 最后一个0不取
        rand_len = random.choice(range(2, len(compete_solution[rand_idx:])))
        code_segment = compete_solution[rand_idx: rand_idx+rand_len]
        # 其他位置处的编码重新排序
        visited_node = list(set(code_segment) - {0})
        unvisited_node = list(set(sorted(random.sample(range(1, demand_node_num+1), demand_node_num))) - set(visited_node))
        # 判断当前已选编码段包含几辆车的行驶路径
        for idx, node in enumerate(code_segment):
            if (idx + 1 != len(code_segment)) and (node == 0):
                if code_segment[-1] == 0:
                    pending_code_segment = code_segment[idx + 1:-1]
                else:
                    pending_code_segment = code_segment[idx + 1:]
                origin_solution_seed = code_segment[:idx]
        random.shuffle(unvisited_node)
        temp_solution_seed = pending_code_segment + unvisited_node
        check_res, new_solution_seed = get_feasible_solution(temp_solution_seed)
        if check_res:
            solution_seed = origin_solution_seed + new_solution_seed
            # 考虑车辆数量限制
            count = 0
            vehicle_num = Data_class.vehicle_num
            switch_vehicle_num = Data_class.switch_vehicle_num
            if switch_vehicle_num == 1:

                for node in solution_seed:
                    if node == 0:
                        count += 1
            if count <= vehicle_num + 1:
                return solution_seed

在竞争操作邻域中能量最大的智能体将自身优秀的编码片段移植给当前智能体,从而产生新的编码段后,需要判断新生成的编码段是否满足车辆数量限制。

第三处是智能体的自学习。自学习产生新的智能体后,要用函数check_feasible_solution()检查新的智能体是否为可行解。

函数check_feasible_solution()代码如下:

def check_feasible_solution(solution_seed):
    switch_vehicle_num = Data_class.switch_vehicle_num
    if switch_vehicle_num == 1:
        count = 0
        vehicle_num = Data_class.vehicle_num
        for node in solution_seed:
            if node == 0:
                count += 1
        if count > vehicle_num + 1:
            return False
    quantity_list = []
    arrive_time_list, leave_time_list = [0], [0]
    demand_quantity = Data_class.demand_quantity
    vehicle_capacity_max = Data_class.vehicle_capacity_max
    travel_time_graph = Data_class.travel_time_graph
    demand_time_window = Data_class.demand_time_window
    demand_service_time = Data_class.demand_service_time
    for i in range(len(solution_seed)-1):
        if solution_seed[i+1] != 0:
            quantity_list.append(demand_quantity[0, solution_seed[i+1]])
            travel_time = travel_time_graph[solution_seed[i], solution_seed[i+1]]
            # 离开上一个节点的时间+上一个节点到当前节点的行驶时间=到达当前节点的时间
            if solution_seed[i] == 0:
                quantity_list = []
                arrive_time_list, leave_time_list = [0], [0]
            arrive_curnode_time = leave_time_list[-1] + travel_time
            arrive_time_list.append(arrive_curnode_time)
            leave_curnode_time = arrive_curnode_time + demand_service_time[solution_seed[i+1]]
            leave_time_list.append(leave_curnode_time)
            # 先判断是否超过车辆的最大载重量限制,再判断是否满足时间窗要求
            if np.sum(np.array(quantity_list)) > vehicle_capacity_max:
                # 超过车辆的最大载重量限制
                return False
            else:
                # 判断是否满足时间窗要求
                time_window_lower = demand_time_window[0, solution_seed[i+1]]
                time_window_upper = demand_time_window[1, solution_seed[i+1]]
                if (arrive_time_list[-1] < time_window_lower) or (arrive_time_list[-1] > time_window_upper):
                    # 早于或晚于当前节点时间窗到达
                    return False
    return True

在进入函数check_feasible_solution()后立即判断生成的解中配送中心的个数是否大于车辆数量加1,如果成立则认为此解为非可行解,直接退出函数避免下面的车辆载重量及时间窗约束条件的判断。

算例验证

沿用我在博文《多智能体进化算法求解带时间窗的VRP问题(python)》最后的计算结果。

修改程序代码增加车辆数量限制最多3辆车后,本文多智能体进化算法在某一次运算求解得到的最优目标函数值和文献一致都为103.6min,且派遣3辆车的配送路径一致。

增加车辆数量限制条件后,本文的多智能体进化算法在某一次运算求解得到的最优解结构如下:
minvalue 98.6min
best_feasible_solution [0, 2, 8, 11, 7, 13, 0, 1, 3, 5, 12, 9, 0, 6, 4, 10, 0]
total_travel_time 90.5min
total_wait_time 104.0min
在这里插入图片描述
增加车辆数量限制条件后,在生成初始可行解及智能体的竞争和自学习操作中可以过滤掉更多的不可行解,所以上面的迭代图显示能够在更短的迭代次数内找到最优解。与文献的最优结果对比,从以上结果可以看出本文设计的多智能体进化算法是有效的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

草莓仙生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值