混合贪心算法求解地铁线路调度

news2024/9/21 10:48:48

一、问题描述

城市轨道交通的繁荣发展,带来了车辆资源需求的日益增加。如何兼顾运营服务水平和运营成本,以最少的车底优质地完成运输任务成为一大严峻问题。本题在后续的描述中将由多辆动车和拖车组合而成的车组称为车底。在日常的运营组织中,车底运用计划指导着所有的车底有序的完成运输任务,是运输计划中非常重要一项。较多的车底运用数可以从容的面对各种客流压力,深受乘客的欢迎却往往不能发挥车底的运输能力,导致运营成本骤升;而车底过少又极易引起车底超负荷运转,缩短设备寿命,面对大客流情况还有可能出现运营紊乱,乘客体验差。因此,车底运用计划对协调运营质量和运营成本意义重大。基于列车运行时刻表,结合列车车底数量、车辆段或停车场(以下统称为车场)的数量、车场的分布状况及容纳能力等,对每一车底在何时何地驶出车场、担当哪些车次以及返回车场作出合理的安排;同时,根据车底检修相关规程,对每一车底的检修时间、检修地点、检修级别等作出具体安排,保证安全、高效、有序的完成日常运营任务。

现在假设有一个地铁运营线,该线网由 20 座车站构成, 本线路全周转时间:120min30s;共有 35 组,停车场的存车能力为 25,车辆段的存车能力为 20;列车运行间隔:早高峰 3min(7点到9点),平峰 8min,晚高峰3min25s(17点到20点),其中,大交路 1-20,小交路1-15,早晚高峰的追踪间隔均指小交路的追踪间隔;列车通过 9 号车站进出车辆段,出入段时间为 2min40s;1,15,20 号车站具备折返能力,其最短折返时间分别为:6min、4min30s、4min30s;1号车
站连接停车场,停车场的出入时间 2min。
在这里插入图片描述
简而言之,要求就是使用最少的车底完成所有车次的牵引计划,同时每辆车的牵引时长要尽量均衡。

二、数据介绍

车次数据分位上行车次和下行车次,上行车次表示从编号较大的站到编号较小的站,下行车次从编号较小的站到编号较大的站,流入从1到20是下行车次,从20到1是上行车次。每天的上、下行车次数各为244。
上行车次
下行车次

三、算法求解

在这一小节中主要介绍两部分内容,第一部分内容是如何获得使用车底尽量少的车底牵引方案,第二部分是如何在保证车底数量不变的前提下,使得每个车底的牵引时间更加均衡。

3.1 车底数量求解

贪心算法
对于此类问题,一个直观的想法采用贪心的策略,对于对于每一个车底,都使得牵引尽量多的车次,直到所有的车次完成牵引,这个时候用到的车底数量就是贪心算法得到的最少车底数量,基本算法流程如下:

step1、初始化使用车底集合为C={},当前车底牵引车次集合D={},剩余车次集合P为上下行全量车次;
step2、按照时空接续关系生成所有可行车次集合,如果可行车次为空,转step4;
step3、随机选择可行接续车次作为车底即将牵引的车次d,D=D+d,P=P-d,转step2;
step4 、如果剩余车底集合P为空,转step6,否则转step5;
step5、更新使用车底集合C,新增车底,转step2;
step6、输出车底牵引计划;
step7、结束。

其中,step2中的时空接续关系是指车底前后牵引的两趟车次在时间和空间上都必须是能够衔接的,在空间上前车的终点站必须是后撤的起点站,在时间上前车的到达时间必须早于后车的出发时间,且时间间隔要满足特定要求。

贪心算法最终结果为使用车底数量41,由于step3中随机选择当前车底的可行车次,导致每个车底的牵引车辆数相对有限,考虑结合整数规划模型来获得当前车底的最优解决方案。

混合贪心算法
step1、初始化使用车底集合为C={},当前车底牵引车次集合D={},剩余车次集合P为上下行全量车次;
step2、计算剩余所有车次的前序车次数量和后续车次数量;
step3、按照车辆的前序车次数和后续车次数进行排序;
step4、将前序车次数和后续车次数量最少的车辆作为必选车次,建立整数规划模型,获取最优路径,最大化彻底服务车次数;
step5、将车底牵引计划添加到车底集合中,更新剩余车次,如果所有列车牵引完成,转step6,否则转step2;
step6、输出车底牵引计划;
step7、结束。

混合贪心算法最终使用的车底数量为36,相比于贪心算法,混合贪心算法主要在两个方面有所改进。一个是优先选择车前序和后续车次少的车次进行分配,这是由于到了算法后期,这样的车次往往一个车次就要完全占据一个车底。另外一个改进的点是采用整数规划,能够真正意义上获取每个车底当前的最优选择。
在这里插入图片描述
从上述甘特图中也可以明显看出贪心策略的一些特征,排序靠前的车底,由于有比较大的选择空间,牵引路径都比较长,牵引负载比较大,排序靠后的车底,由于选择空间急剧收缩,往往一个车底只能服务一两趟车次。

3.2 负载均衡调整

由于贪心策略产生的结果会出现负载极度不均衡的情况,考虑采用局部调整策略对产生的方案进行调整。调整的思路很简单,将负载比较高的车底服务的车次重新分配给负载比较低的车底即可,其对应的基本流程如下:
在这里插入图片描述
调整过后的结果如下,从图上可以看出来调整之后的结果变得更加均衡了。在这里插入图片描述

四、代码实现

4.1 贪心算法
def get_path(all_trains,from_trains,to_trains):
    used_trains=[]
    all_path=[]
    cnt=0

    max_length=22
    max_num=[randint(18,22) for i in range(1000)]
    
    
    while len(set(used_trains))!=len(all_trains):
        route_length=min(max_length,max_num[cnt])
        for i in range(route_length,0,-1):
            result=solver_route(all_trains,from_trains,to_trains,used_trains,i)
            if result:
                break
            else:
                max_length=i-1

        used_trains+=result
        all_path.append(result)
        cnt+=1
    return all_path
4.2 整数规划
def solver_route(all_trains,from_trains,to_trains,used_trains,route_length):
    train_num=len(all_trains)


    # 创建模型
    model = gp.Model('train_scheduling')
    
    # 数据处理
    new_from_trains=deepcopy(from_trains)
    new_to_trains=deepcopy(to_trains)
    for train in all_trains:
        new_from_trains[train]=list(set(new_from_trains[train])-set(used_trains))
        new_to_trains[train]=list(set(new_to_trains[train])-set(used_trains))
    
    io_num={}
    for i in range(len(all_trains)):
        train=all_trains[i]
        io_num[i]=len(new_from_trains[train])+len(new_to_trains[train]) 
        
    dff=pd.DataFrame(io_num.items(),columns=['key','value'])
    sort_trains=dff.sort_values(by=['value'],ascending=False)['key'].values
    
    fix_train=-1
    for train in sort_trains:
        if not train in used_trains:
            fix_train=train
            break
    

    # 是否将站点i放置在位置j
    x = {}
    for i in range(train_num):
        for j in range(route_length):
            x[i,j] = model.addVar(vtype=GRB.BINARY, name=f'x_{i}_{j}')



    # 目标函数
    obj=0
    for i in range(train_num):
        for j in range(route_length):
            obj+=io_num[i]*x[i,j]
    model.setObjective(obj,GRB.MINIMIZE)


    # 每个位置最多有一个节点
    for j in range(route_length):
        expr=0
        for i in range(train_num):
            expr+=x[i,j]
        model.addConstr(expr==1)



    # 节点之间要有连关系
    for j in range(route_length-1):
        for i  in range(train_num):
            expr=0
            train=all_trains[i]
            for ii in from_trains[train]:
                expr+=x[ii,j]
            model.addConstr(expr>=x[i,j+1])

    # 遍历过的车次不再选择
    for i in used_trains:
        expr=0
        for j in range(route_length):
            expr+=x[i,j]
        model.addConstr(expr<=0)
        
    # fix train一定要满足
    expr=0
    for j in range(route_length):
        expr+=x[fix_train,j]
    model.addConstr(expr==1)



    # 求解
    model.setParam('OutputFlag', 0)
    model.setParam(GRB.Param.TimeLimit, 60)
    model.optimize()

    # 检查求解器状态
    status = model.Status
    if status == GRB.INFEASIBLE:
        return []
    else:
        return get_result(x,train_num,route_length)
局部调整
paths=[]
for idx,row in result.iterrows():
    path=eval(row[0])
    paths.append(path)
    
for i in range(len(paths)):
    path=paths[i]
    remove_trains=[]
    path_duration=get_path_duration(path)
    print(path_duration)
    for train in path:
        if path_duration<=avg:
            break
        for j in range(i+1,len(paths)):
            add_path=paths[j]
            add_path_duration=get_path_duration(add_path)
            if add_path_duration>avg:
                continue
            idx=get_insert_index(train,add_path)
            if idx>0:
                add_path.insert(idx,train)
                remove_trains.append(train)
                path_duration-=(train_info.end_time-train_info.start_time)
                break
    for train in remove_trains:
        paths[i].remove(train)

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

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

相关文章

算力狂飙|WAIC 2024上的服务器

7月7日&#xff0c;2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC 2024&#xff09;在上海落下帷幕。这场备受瞩目的AI盛宴与热辣夏日碰撞&#xff0c;吸引了全球科技、产业及学术界的广泛关注&#xff0c;线下参观人数突破30万人次&#xff0c;线上流量突…

DOM(文档对象模型)生命周期事件

前言 DOM 生命周期事件涉及到从创建、更新到销毁 DOM 元素的不同阶段。 ● 我们来看下当HTML文档加载完再执行JavaScript代码 document.addEventListener(DOMContentLoaded, function (e) {console.log(HTML parsed adn DOM tree built!, e); })● 除此之外&#xff0c;浏览…

Linux | 安装lb-toolkits 1.2.4库

Linux | 安装 lb-toolkits 最近又需要下载葵花的数据&#xff0c;之前分享过一次代码。今天发现之前的环境不小心被我删了&#xff0c;而运行相关的代码需要安装lb-toolkits这个库&#xff0c;今天正好记录了一下安装lb-toolkits的过程。 这里安装的版本是1.2.4&#xff0c;别…

【前端从入门到精通:第十二课: JS运算符及分支结构】

JavaScript运算符 算数运算符 关于自增自减运算 自增或者自减运算就是在本身的基础上进行1或者-1的操作 自增或者自减运算符可以在变量前也可以在变量后&#xff0c;但是意义不同 自增自减运算符如果在变量前&#xff0c;是先进行自增或者自减运算&#xff0c;在将变量给别人用…

基于springboot+vue实现的厨艺交流平台(文末源码+Lw)093

93基于SpringBootVue的实现的厨艺交流平台&#xff08;源码数据库万字Lun文流程图ER图结构图演示视频软件包&#xff09; 系统功能&#xff1a; 这次开发的厨艺交流平台功能有个人中心&#xff0c;食材分类管理&#xff0c;用户管理&#xff0c;菜品分类管理&#xff0c;菜谱信…

力扣第一题

1. 两数之和 提示 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可…

CSS技巧专栏:一日一例 2.纯CSS实现 多彩边框按钮特效

大家好,今天是 CSS技巧一日一例 专栏的第二篇《纯CSS实现多彩边框按钮特效》 先看图: 开工前的准备工作 正如昨日所讲,为了案例的表现,也处于书写的习惯,在今天的案例开工前,先把昨天的准备工作重做一遍。 清除浏览器的默认样式定义页面基本颜色设定body的样式清除butt…

深入理解 Qt 的 `moveToThread`:提升多线程应用性能的关键

&#x1f60e; 作者介绍&#xff1a;欢迎来到我的主页&#x1f448;&#xff0c;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff08;领取大厂面经等资料&#xff09;&#xff0c;欢迎加我的…

选择排序(C语言版)

选择排序是一种简单直观的排序算法 算法实现 首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置。 再从剩余未排序元素中继续寻找最小&#xff08;大&#xff09;元素&#xff0c;然后放到已排序序列的末尾。 重复第二步&…

CTF php RCE (一)

0x01 引言 首先进入题目 应该是大部分都是一段白盒PHP审计&#xff0c;然后我们为了命令执行&#xff0c;绕过或者是钻空子等等操作&#xff0c;来拿到flag 0x02 基础 0x01 传参方式 这里有两个工具&#xff0c;hackbar和burpsuite,这两个工具非常实用 大家可以自己Googl…

Linux 利用命名空间创建一个自己的“容器“

Linux 利用命名空间创建一个自己的"容器" 前置条件 创建一个目录存放容器mkdir /myapp准备静态编译busybox&#xff0c;操作系统自带的往往是依赖动态库的(本文使用的debian apt install busybox-static) 开始 使用unshare起一个独立命名空间.# 进入后/myapp目录…

办公助手推荐✨

办公助手来啦&#xff01;✨ 办公助手来啦&#xff01;✨&#x1f31f; 主要亮点&#x1f4dd; 全新PDF编辑器&#x1f3a8; 丰富的幻灯片版式&#x1f30d; 改进的从右至左显示&#x1f310; 新增本地化选项 &#x1f4ca; 应用场景在线办公套件&#x1f4f1; 多平台支持&…

【YOLO8系列】(二)YOLOv8环境配置,手把手嘴对嘴保姆教学

目录 一. 准备环境 1.Anaconda下载 2.创建yolov8虚拟环境 3.pytorch安装 4.CUDA下载 5.CUDNN下载 二、yolov8模型下载 1.clone模型 2.pycharm配置 ①解释器配置 ②终端配置 3.安装必要库 4.下载训练模型 三、 环境验证 四、总结 YOLOv8 是 YOLO 系列最新的目标…

Springboot各个版本维护时间

Springboot各个版本维护时间

大话光学原理:1.“实体泛光说”、反射与折射

一、实体泛光说 在古希腊&#xff0c;那些喜好沉思的智者们中&#xff0c;曾流传着一个奇妙的设想&#xff1a;他们认为&#xff0c;我们的眼睛仿佛伸出无数触手般的光线&#xff0c;这些光线能向四面八方延伸&#xff0c;紧紧抓住周围的每一个物体。于是&#xff0c;当我们凝视…

Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)

Linux&#xff1a;进程间通信&#xff08;二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量&#xff09; 上次结束了进程间通信一&#xff1a;Linux&#xff1a;进程间通信&#xff08;一.初识进程间通信、匿名管道与命名管道、共享内存&#xff09; 文章目录 …

分布式IO模块软件配置

组态接口模块 1、打开网络视图 2、拖拽出ET200SP 3、双击ET200SP的图片&#xff0c;进入从站配置 总线适配器的组态更换 关于IO地址分配&#xff0c;需要建立好子网通信后&#xff0c;在主机上配置。 可以看到IP 和设备名 设备与控制器的Profinet连接 先找到设备名称再找…

H桥驱动器芯片详解

H桥驱动器芯片详解 上一篇文章讲解了H桥驱动器的控制原理&#xff0c;本文以汽车行业广泛应用的DRV8245芯片为例&#xff0c;详细讲解基于集成电路的H桥驱动器芯片。 1.概述 DRV824x-Q1系列器件是德州仪器&#xff08;TI&#xff09;的一款专为汽车应用设计的全集成H桥驱动器…

Linux——开发工具

1.yum yum是centos中的一个软件下载安装管理客户端&#xff0c;可以下载需要的软件或者解决依赖关系问题&#xff08;如动态库&#xff09;。程序都是来源于一段源代码&#xff0c;为了方便下载&#xff0c;源代码被提前在不同的环境下编译好生成对应的yum软件包&#xff0c;存…

微信小程序毕业设计-书店系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…