07.AOE网和图的关键路径

2023-12-30 01:50
文章标签 路径 关键 07 aoe 网和图

本文主要是介绍07.AOE网和图的关键路径,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

相关代码地址:https://gitee.com/gudongkun/datestruct

一、什么是AOE网

AOE(Activity on edge network) :在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图叫做边表示活动的网,简称AOE网

AOE网中没有入边的顶点称为始点(或源点);没有出边的点称为终点(或汇点)

二、AOE网的性质

  1. 只有再某顶点所代表的事件发生后,从该顶点出发的活动才能开始;
  2. 只有再进入某顶点的各活动都结束,该顶点所代表的事件才能发生。

三、AOE网可以解决下列问题

  1. 完成整个工程至少需要多少时间
  2. 为缩短完成工程所需要的时间,应当加快哪些活动

四、关键活动

1.关键路径

从开始到结束最长的一条路径,可能不只一条,那么如何找到这样条路径呢?这就需要,找本节的重要概念关键活动 (全部由关键活动组成的路径,就是关键路径)

2.关键活动

(1)如果缩短某一条活动的时间,不能改变总体结束时间,活动就不是关键活动。
(2)如果缩短某条活动的时间,能减少总体结束时间,活动就是关键活动。
(3)活动的最早发生时间,和最迟发生时间一样,活动,就是关键活动。

五、求关键活动

上文说到,活动的最早发生时间,和最迟发生时间一样,活动,就是关键活动。
这两个时间怎么求呢?下面将给出答案。

1.关键活动的4个前导量

(1)事件A的最早发生时间event_early[B] :event_early[B] = max{event_early[BeforeB]+len<BeforeB,B>}
(2)事件A的最迟发生时间event_latest[B]:event_latest[B] = min{event_latest[AfterB]-len<B,AfterB>}
(3)活动AB 的最早发生时间 activity_early :activity_early[BC] = event_early[B]
(4)活动AB 的最迟发生时间 activity_latest : activity_latest[CD] = event_latest[D] - len[CD]

注:len<A,B>表示 弧AB的长度

img

(1) event_early分析

event : A B C D E F G H I
event_early : 0 6 4 5 7 7 16 14 18

  1. 从起点开始算,起点时间一定是0
  2. 之后的事件用前一个事件的时间推算如:
    1. event_early[B] = max{event_early[A]+len<A,B>}
    2. event_early[E] = max{event_early[B]+len<B,E> ,event_early[C]+len<C,E>}
(2)event_latest分析,依赖event_early

event : A B C D E F G H I
event_early : 0 6 4 5 7 7 16 14 18
event_latest: 0 6 6 8 7 10 16 14 18

  1. 从后往前计算,结束事件直接取event_early
  2. 用前一个事件的时间推算如:event_latest[B] = min{event_latest[C]-len<A,B>}
  3. 开始时间,必定是0
(3)activity_early 分析

活动BC 的最早开始时间应该等于 时间B的最早开始时间因此有:activity_early[BC] = event_early[B]

 event       : A B  C D  E  F  G  H  Ievent_early : 0 6  4 5  7  7  16 14 18event_latest: 0 6  6  8 7 10 16 14 18activity       : AB  AC   AD    BE  CE  DF  EG  EH  FH  GI  HI            activity_early : 0    0    0    6   4   5   7   7   7   16  14
(4) activity_latest分析

img

activity_latest 要从前往后算

例如活动CD 的最晚开始时间要保证时间D的最迟时间不能拖后所有:activity_latest[CD] = event_latest[D] - len[CD]

 event       : A B C D E  F  G  H  Ievent_early : 0 6  4 5  7  7  16 14 18event_latest: 0 6  6  8 7 10 16 14 18activity       : AB  AC   AD   BE   CE  DF  EG  EH  FH  GI  HI            activity_early : 0    0    0    6   4   5   7   7   7   16  14activity_latest: 0    2    3    6   6   8   7   11  10  16  14
2.关键活动:

最早开始时间和最晚开始时间是一样的称为关键活动。

 activity      : (AB)  AC  AD  (BE)  CE  DF (EG) (EH)  FH  (GI)  (HI)            activity_early : 0    0    0    6   4   5   7    7    7    16    14activity_latest: 0    2    3    6   6   8   7    7    10   16    14

img

注意:

  1. 关键活动组成的路径叫关键路径
  2. 虽然会产生多条关键路径,但是多条关键路径的执行时间是一样的。
  3. 工程的总执行时间就是任意选其中一条关键路径的总执行时间。

六、动画演示

https://www.bilibili.com/video/BV1PW41187vc

七、代码实现

aoe.go

package aoeimport ("fmt""gitee.com/gudongkun/datestruct/dataStructures/graph""gitee.com/gudongkun/datestruct/dataStructures/linear"
)func GetGraph() graph.DMGraph {g := graph.NewDMGraph()g.AddNode("A")g.AddNode("B")g.AddNode("C")g.AddNode("D")g.AddNode("E")g.AddNode("F")g.AddNode("G")g.AddNode("H")g.AddNode("I")g.AddEdge("A", "B", 6)g.AddEdge("A", "C", 4)g.AddEdge("A", "D", 5)g.AddEdge("B", "E", 1)g.AddEdge("C", "E", 1)g.AddEdge("E", "G", 9)g.AddEdge("E", "H", 7)g.AddEdge("G", "I", 2)g.AddEdge("H", "I", 4)g.AddEdge("D", "F", 2)g.AddEdge("F", "H", 4)return g
}func AOEKeyEvents() []string {g := GetGraph()eventEarly := make(map[string]int)eventLatest := make(map[string]int)activeEarly := make(map[string]int)activeLatest := make(map[string]int)// 1. 求eventEarlyindegrees := make(map[string]int)stack := linear.NewStack()for _, v := range g.Nodes {indegrees[v] = g.GetIndegree(v)if indegrees[v] == 0 {stack.Push(v)indegrees[v] = -1eventEarly[v] = 0}}for !stack.IsEmpty() {v, _ := stack.Pop()for _, edge := range g.GetEdgesByHead(v) {indegrees[edge.Tail]--if indegrees[edge.Tail] == 0 {stack.Push(edge.Tail)indegrees[edge.Tail] = -1for _, endEdge := range g.GetEdgesByTail(edge.Tail) {val, ok := eventEarly[edge.Tail]if !ok {eventEarly[edge.Tail] = eventEarly[endEdge.Head] + endEdge.Val} else {if val < eventEarly[endEdge.Head]+endEdge.Val {eventEarly[edge.Tail] = val}}}}}}// 2. 求eventLatestoutdegrees := make(map[string]int)outstack := linear.NewStack()for _, v := range g.Nodes {outdegrees[v] = g.GetOutdegree(v)if outdegrees[v] == 0 {outstack.Push(v)outdegrees[v] = -1eventLatest[v] = eventEarly[v]}}for !outstack.IsEmpty() {v, _ := outstack.Pop()for _, edge := range g.GetEdgesByTail(v) {outdegrees[edge.Head]--if outdegrees[edge.Head] == 0 {outstack.Push(edge.Head)outdegrees[edge.Head] = -1for _, endEdge := range g.GetEdgesByHead(edge.Head) {val, ok := eventLatest[edge.Head]if !ok {eventLatest[edge.Head] = eventLatest[endEdge.Tail] - endEdge.Val} else {if val < eventLatest[endEdge.Tail]-endEdge.Val {eventLatest[edge.Head] = val}}}}}}// 3.求 activityEarlyfor _, v := range g.GetEdgeList() {activeEarly[v.Head+"-"+v.Tail] = eventEarly[v.Head]}// 4. 求activeLatestfor _, v := range g.GetEdgeList() {activeLatest[v.Head+"-"+v.Tail] = eventLatest[v.Tail] - v.Val}// 5.关键活动var keyActivity []stringfor k,v := range activeEarly {if activeLatest[k] == v {keyActivity = append(keyActivity,k)}}fmt.Println(keyActivity)return keyActivity}

aoe_test.go

package aoeimport "testing"func TestAOEKeyEvents(t *testing.T) {list := AOEKeyEvents()if len(list) != 6 {t.Fail()}
}

添加了新方法的 有向图 dmgraph.go

package graphimport ("errors"
)const DMaxSize = 20
const DMaxNum = 99999type DMGraph struct {Edges   [DMaxSize][DMaxSize]intEdgeNum intNodes   []stringIndexs  map[string]int
}type DEdge struct {Head, Tail stringVal        int
}func NewDMGraph() DMGraph {var g DMGraphfor k, v := range g.Edges {for kk, _ := range v {if k == kk {g.Edges[k][kk] = 0} else {g.Edges[k][kk] = DMaxNum}}}g.Indexs = make(map[string]int)return g}func (g *DMGraph) AddNode(nodeName string) error {if g.Indexs == nil {return errors.New("不是有效的图")}if _, ok := g.Indexs[nodeName]; ok {return errors.New("已经添加过此结点")}g.Indexs[nodeName] = len(g.Nodes)g.Nodes = append(g.Nodes, nodeName)return nil
}func (g *DMGraph) AddEdge(Head, Tail string, val int) error {if _, ok := g.Indexs[Head]; !ok {return errors.New("结点不存在:" + Head)}if _, ok := g.Indexs[Tail]; !ok {return errors.New("结点不存在:" + Tail)}if g.Edges[g.Indexs[Head]][g.Indexs[Tail]] != DMaxNum {return errors.New("边已经存在")}g.Edges[g.Indexs[Head]][g.Indexs[Tail]] = valg.EdgeNum++return nil
}func (g *DMGraph) GetEdgeList() []DEdge {var edgeList []DEdgefor i := 0; i < len(g.Nodes); i++ {for j := 0; j < len(g.Nodes); j++ {if g.Edges[i][j] != DMaxNum && i != j {edgeList = append(edgeList,DEdge{Head: g.Nodes[i], Tail: g.Nodes[j], Val: g.Edges[i][j]},)}}}return edgeList
}func (g *DMGraph) GetIndegree(ele string) int {tail, ok := g.Indexs[ele]if !ok {return -1 //点不存在}indegree := 0for i := 0; i < len(g.Nodes); i++ {if g.Edges[i][tail] != 0 && g.Edges[i][tail] != DMaxNum {indegree++}}return indegree
}func (g *DMGraph) GetOutdegree(ele string) int {head, ok := g.Indexs[ele]if !ok {return -1 //点不存在}outdegree := 0for i := 0; i < len(g.Nodes); i++ {if g.Edges[head][i] != 0 && g.Edges[head][i] != DMaxNum {outdegree++}}return outdegree}func (g *DMGraph) GetEdgesByTail(ele string) []DEdge {var edgeList []DEdgetail, ok := g.Indexs[ele]if !ok {return nil}for i := 0; i < len(g.Nodes); i++ {if g.Edges[i][tail] != 0 && g.Edges[i][tail] != DMaxNum {edgeList = append(edgeList,DEdge{Head: g.Nodes[i], Tail: g.Nodes[tail], Val: g.Edges[i][tail]},)}}return edgeList
}func (g *DMGraph) GetEdgesByHead(ele string) []DEdge {var edgeList []DEdgehead, ok := g.Indexs[ele]if !ok {return nil}for i := 0; i < len(g.Nodes); i++ {if g.Edges[head][i] != 0 && g.Edges[head][i] != DMaxNum {edgeList = append(edgeList,DEdge{Head: g.Nodes[head], Tail: g.Nodes[i], Val: g.Edges[head][i]},)}}return edgeList
}

这篇关于07.AOE网和图的关键路径的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/551340

相关文章

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

VSCode设置python SDK路径的实现步骤

《VSCode设置pythonSDK路径的实现步骤》本文主要介绍了VSCode设置pythonSDK路径的实现步骤,包括命令面板切换、settings.json配置、环境变量及虚拟环境处理,具有一定... 目录一、通过命令面板快速切换(推荐方法)二、通过 settings.json 配置(项目级/全局)三、

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)

《如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)》:本文主要介绍如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)问题,具有很好的参考价值,希望对大家有所帮助,如有... 目录先在你打算存放的地方建四个文件夹更改这四个路径就可以修改默认虚拟内存分页js文件的位置接下来从高级-

一文详解如何查看本地MySQL的安装路径

《一文详解如何查看本地MySQL的安装路径》本地安装MySQL对于初学者或者开发人员来说是一项基础技能,但在安装过程中可能会遇到各种问题,:本文主要介绍如何查看本地MySQL安装路径的相关资料,需... 目录1. 如何查看本地mysql的安装路径1.1. 方法1:通过查询本地服务1.2. 方法2:通过MyS

springboot实现配置文件关键信息加解密

《springboot实现配置文件关键信息加解密》在项目配置文件中常常会配置如数据库连接信息,redis连接信息等,连接密码明文配置在配置文件中会很不安全,所以本文就来聊聊如何使用springboot... 目录前言方案实践1、第一种方案2、第二种方案前言在项目配置文件中常常会配置如数据库连接信息、Red

Python如何调用指定路径的模块

《Python如何调用指定路径的模块》要在Python中调用指定路径的模块,可以使用sys.path.append,importlib.util.spec_from_file_location和exe... 目录一、sys.path.append() 方法1. 方法简介2. 使用示例3. 注意事项二、imp

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想