草庐IT

AOE网络关键路径求解的通俗理解

一只coding猪 2024-01-05 原文

AOE网络是一种以边主导的活动网络,简单来说是一种有向加权图,通常有一个起点(源点)和一个终点(汇点),中间顶点表示某个事件,或叫里程碑,而有向边叫做活动,AOE网络通常可以用来表达某些带有前驱后继关系的一系列活动。关键路径是指影响最终活动进程的所有活动构成的路径,求解AOE网络的关键路径,可以使活动时间安排更合理,也更容易找出网络中的瓶颈并加以改进。

1.从番茄炒蛋出发

番茄炒蛋想必很多人都做过,虽然过程可能有点差异,但是主流做法基本一致,下面以一顿饭为例,来说明AOE关键路径的计算。请看下图

请忽略其中不大合乎现实的点哈,比如煮饭只需要8分钟。。。

所谓关键路径,就是从源点到汇点,某个路径上的活动,直接决定了最早的开饭时间。AOE网络图看起来不太直观,下面将它转化成时序图

从这张图中,我们可以很直观的看出来关键路径,如果加盐出锅可以快1分钟,我们就可以早1分钟吃饭,但煮饭时间再缩短1分钟,对提前吃饭是没有帮助的。继续分析下去,我们发现【番茄去皮->番茄切块->炒鸡蛋->鸡蛋下锅->加盐出锅】是整个活动网络的关键路径。那既然这么直观就能看出来,为什么还要求解呢?

因为这是为了讲解AOE网络而构造的简单有向图,实际应用中的网络可能更复杂,很难直观看出结果。此外,有时网络活动的耗时是动态变化的,例如把上图中的事件想像成异步管道(例如kafka),把活动想像成发布或订阅数据的处理程序,处理程序的耗时可能随着代码的改进而变化,这就需要通过关键路径算法来找出瓶颈活动并加以改进。

1.1.松弛时间与关键路径

示例中不难发现,有些事件或活动,是有灵活调整的空间的。比如鸡蛋打散,即使晚个2分钟进行,也不影响后面炒鸡蛋的开始时间,这种非关键路径上的活动或事件可以调整的现象,叫做松弛。关键路径的主要特征就是,其路径上的活动或事件不允许松弛,因为一旦移动关键活动或事件,就会影响开饭的时间(干饭人不允许这种情况发生!)

可以得出一个结论,可松弛的活动具有这样的特点,它的最早开始时间和最晚开始时间是可以不相等的。而关键路径活动,这两个时间一定是相等的。所以找出关键路径的问题就变成了找到所有活动的最早可以开始时间和最迟可以开始的时间。因为活动的权重是固定的,所以可以不用关注活动的最早/最迟结束时间。

2.事件的开始时间

在有向图中活动即是有向边,而事件就是表达有向边的顶点,所以活动的开始时间,与事件的开始时间紧密相关。先来看下事件的开始时间。
事件有两种开始时间,一种是最早开始时间,用 E e Ee Ee表达,一种是最迟开始时间,用 E l El El表达。

2.1.最早开始时间 E e Ee Ee

事件A要开始的前提是,它之前的活动都完成,跟A事件之后或不经过A的其他活动无关,比如A4备菜完毕这个事件,取决于前面最长的番茄处理过程,即5+2=7min

用公式来表达就是:
E e [ A ] = m a x ( E e [ A p r e ] + W < A p r e , A > ) Ee[A] = max(Ee[A_{pre}]+W<A_{pre},A>) Ee[A]=max(Ee[Apre]+W<Apre,A>)
E e [ A p r e ] Ee[A_{pre}] Ee[Apre]是A事件的所有前驱事件顶点, W < A p r e , A > W<A_{pre},A> W<Apre,A>是A的前驱事件到A的活动权重。

计算方式是,从源点出发进行BFS,每次遇到顶点A,都将A的到达时间更新为更大的值,一直计算到汇点。最终计算最早开饭时间为13min。

2.2.最迟开始时间 E l El El

因为我们希望不要延迟开饭的时间,所以El的含义是,为了保障开饭这个事件的 E e [ A 9 ] Ee[A9] Ee[A9]时间前提下,A不能晚于什么时间开始。还是以A4为例,要保证最早的开饭时间 E e [ A 9 ] Ee[A9] Ee[A9],A6就不能晚于11min,A5不能晚于10min,A4不能晚于7min。为什么不是8分钟呢?因为A4-A5-A6这条路径要比A4-A6更长,所以A4-A5-A6这条线是影响开饭时间的关键活动。

因此,影响El的是事件A到汇点中最长的路径,用公式来表达如下:
E l [ A ] = m i n ( E l [ A n e x t ] − W < A , A n e x t > ) El[A] = min(El[A_{next}]-W<A,A_{next}>) El[A]=min(El[Anext]W<A,Anext>)
这个计算也很简单,只需要设 E l [ 汇点 ] = E e [ 汇点 ] El[汇点]=Ee[汇点] El[汇点]=Ee[汇点],并从汇点开始沿有向边的逆向进行BFS,计算遇到顶点的El即可。

3.活动的开始时间

掌握了所有事件的 E e Ee Ee E l El El,我们来看活动的最早开始时间 e e e和最迟开始时间 l l l

3.1.活动的最早开始时间

值得庆幸的是,活动的最早开始时间就是其出发顶点的最早开始时间,这很符合直觉。所以这里不需要计算,只需要令 e [ < A , A n e x t > ] = E e [ A ] e[<A,A_{next}>] = Ee[A] e[<A,Anext>]=Ee[A]即可,其中 < A , A n e x t > <A,A_{next}> <A,Anext>是从A出发的活动。

3.2.活动的最迟开始时间

重头戏来了,这个是比较不容易理解的地方。因为活动 < A , A n e x t > <A,A_{next}> <A,Anext>的最迟开始时间,并不等于事件A的最迟开始时间 E l [ A ] El[A] El[A],思考一下这是为什么?

one Mississippi…
two Mississippi…

OK…因为事件A的最迟开始时间,是基于A到开饭的最长路径长度计算得来的。从A出发的活动可能有多个, < A , A n e x t > <A,A_{next}> <A,Anext>未必是这条最长路径上的活动。比如炒番茄这项活动,我们完全可以等炒鸡蛋活动开始1分钟后再开始炒番茄,也不会影响最后的开饭。而炒鸡蛋和炒番茄,都是从A4出发的活动。

所以影响炒番茄活动的,是它的到达顶点 A 6 A6 A6的最迟允许开始时间。其计算方式如下:
l [ < A , A n e x t > ] = E l [ A n e x t ] − W < A , A n e x t > l[<A,A_{next}>] = El[A_{next}] - W<A,A_{next}> l[<A,Anext>]=El[Anext]W<A,Anext>
属于是从活动的终点推导活动的最迟开始时间了,所以从时序图上也能看出,炒番茄这项活动的最早开始时间是7min,最迟开始时间是8min,显然它不是关键路径。

4.求解关键路径

目前我们掌握了 e e e l l l,即所有活动的最早和最迟开始时间,如果上面所述都理解了,那最后的关键路径求解就不用说了吧(不理解的话再仔细看下1.1部分)

重复一下结论,这里的关键路径是【番茄去皮->番茄切块->炒鸡蛋->鸡蛋下锅->加盐出锅】

干饭人开饭!(╹▽╹)

有关AOE网络关键路径求解的通俗理解的更多相关文章

  1. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  2. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  3. ruby-on-rails - Rails - 使用/自定义 URL : '/dashboard' 指定根路径 - 2

    如何使此根路径转到:“/dashboard”而不仅仅是http://example.com?root:to=>'dashboard#index',:constraints=>lambda{|req|!req.session[:user_id].blank?} 最佳答案 您可以通过以下方式实现:root:to=>redirect('/dashboard')match'/dashboard',:to=>"dashboard#index",:constraints=>lambda{|req|!req.session[:user_id].b

  4. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  5. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  6. ruby - Ruby 的 AST 中的 'send' 关键字是什么意思? - 2

    我正在尝试学习Ruby词法分析器和解析器(whitequarkparser)以了解更多有关从Ruby脚本进一步生成机器代码的过程。在解析以下Ruby代码字符串时。defadd(a,b)returna+bendputsadd1,2它导致以下S表达式符号。s(:begin,s(:def,:add,s(:args,s(:arg,:a),s(:arg,:b)),s(:return,s(:send,s(:lvar,:a),:+,s(:lvar,:b)))),s(:send,nil,:puts,s(:send,nil,:add,s(:int,1),s(:int,3))))任何人都可以向我解释生成的

  7. ruby - 如何根据长度将路径数组转换为嵌套数组或散列 - 2

    我需要根据字符串路径的长度将字符串路径数组转换为符号、哈希和数组的数组给定以下数组:array=["info","services","about/company","about/history/part1","about/history/part2"]我想生成以下输出,对不同级别进行分组,根据级别的结构混合使用符号和对象。产生以下输出:[:info,:services,about:[:company,history:[:part1,:part2]]]#altsyntax[:info,:services,{:about=>[:company,{:history=>[:part1,:pa

  8. ruby - 易于初学者理解的 Ruby 库 - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭3年前。Improvethisquestion我正处于学习Ruby的阶段,我想查看一些小型库的源代码以了解它们是如何构建的。我不知道什么是小型图书馆,但希望SO能推荐一些易于理解的图书馆来学习。因此,如果有人知道一两个非常小的库,这是新手Rubyists学习的好例子,请推荐!我想使用Manveru'sInnatelib,因为它试图保持在2000LOC以下,但我还不熟悉其中经常使用的Ruby速记。也许大约100-5

  9. ruby - 无法理解 `puts{}.class` 和 `puts({}.class)` 之间的区别 - 2

    由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A

  10. ruby-on-rails - 如何播种图像的路径? - 2

    Organization和Image具有一对一的关系。Image有一个名为filename的列,它存储文件的路径。我在Assets管道中包含这样一个文件:app/assets/other/image.jpg。播种时如何包含此文件的路径?我已经在我的种子文件中尝试过:@organization=...@organization.image.create!(filename:File.open('app/assets/other/image.jpg'))#Ialsotried:#@organization.image.create!(filename:'app/assets/other/i

随机推荐