關鍵路徑JS實現
我勒個去,畢業論文終於提交了,趕緊完成圖基本演算法的最後一節。。。
1 定義
在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,這種有向圖的邊表示活動的網,稱為AOE網。AOE網中沒有入邊的頂點稱為始點或源點,沒有出邊的頂點稱為終點或匯點。由於一個工程通常總有一個開始和一個結束,因此AOE網只有一個源點和匯點。
與AOV網區別在於,AOV網是頂點表示活動的網,它只描述活動之間的制約關係,而AOE網是用邊表示活動的網,邊上的權值表示活動持續的時間。其是建立在活動之間制約關係沒有矛盾的基礎之上,再來分析整個工程需要多少時間。
我們把路徑上各個活動所持續的時間之和稱為路徑長度,從源點到匯點具有最大長度的路徑叫關鍵路徑,在關鍵路徑上的活動叫關鍵活動。只有找出AOE網中的關鍵路徑,並縮短路徑上關鍵活動的工作時間,才能最有效的縮短完成工程的時間。
2 建立測試網
按照拓撲排序的鄰接表結構,建立測試網:
class vex{
constructor(value){
this.data = value;
this.firstEdge = null;
this.in = 0; //用於存放頂點的入度
}
}
class adjvex{
constructor(node,weight){
this.node = node;
this.weight = weight;
this.next = null;
}
}
class Graph{
constructor(v,vr){
let len = v.length;
let vexs = new Array(len);
let v1=0,v2=0;
let newvex = null;
for (let i=0;i<len;i++){
vexs[i] = new vex(v[i]);
}
for (let arc of vr){
v1 = v.indexOf(arc[0]);
v2 = v.indexOf(arc[1]);
newvex = new adjvex(v2,arc[2]);
newvex.next = vexs[v1].firstEdge; //頭插法
vexs[v1].firstEdge = newvex;
vexs[v2].in++;
}
this.adjList = vexs;
}
}
let a = new Graph(['v0','v1','v2','v3','v4','v5','v6','v7','v8','v9','v10','v11','v12','v13'],[['v0','v11',1],['v0','v4',1],['v0','v5',1],['v1','v4',1],['v1','v8',1],['v1','v2',1],['v2','v5',1],['v2','v6',1],['v3','v2',1],['v3','v13',1],['v4','v7',1],['v5','v8',1],['v5','v12',1],['v6','v5',1],['v8','v7',1],['v9','v11',1],['v9','v10',1],['v10','v13',1],['v12','v9',1]]);
console.log(a);
3 演算法思路
在AOE網中找到關鍵路徑在於如何找到關鍵活動。對此我們定義幾個引數:
- 事件的最早發生時間etv:即頂點代表的事件最早發生的時間
- 事件的最晚發生時間ltv:即頂點代表的事件最晚發生的時間
- 活動最早的開工時間ete:即弧代表的活動最早發生時間
- 活動最晚的開工時間lte:即弧代表的活動最晚發生時間,也就是不推遲工期的最晚開工時間。
例如下圖,有4個事件: v0,v1,v2和v3,以及4個活動:a0,a1,a2和a4。事件v3必須等到活動a2和a4完成以後才能觸發,因此在不推遲工期的條件下,事件v3的最早發生時間和最晚發生時間都是12,且v2的最早和最晚發生時間也都是4,但是對於事件v1,其只要保證在時間7之前發生即可,因此其最早發生時間是3,而最晚發生時間是7。綜上所述,要減少整個工程的時間,對於縮減活動a0和a2的時間並沒有什麼作用,關鍵要縮減經過事件頂點v2的活動a1和a4的時間。因此經過頂點v2的路徑才是關鍵路徑。由此我們可以看出,一個活動是否為關鍵活動在於活動最早的開工時間ete是否等於活動最晚的開工時間lte,若等於,該活動則為關鍵活動,否則則不是。
在關鍵路徑演算法,我們先求得頂點事件的最早和最晚發生時間,再求得弧活動的最早和最晚開工時間,最後比較弧活動的兩者時間是否相等,判斷是否為關鍵活動。
4 程式碼
整個求解關鍵路徑過程可劃分為三部分:
- 求解各頂點事件的最早發生時間
- 求解各頂點事件的最晚發生時間
- 求解各弧上活動的最早和最晚發生時間,並做關鍵活動的判斷
首先求得各頂點事件的最早發生時間,可以看到程式碼與拓撲排序很相似。求各頂點的最早發生時間判定條件如下
例如下圖,etv[1] + a2 < etv[2] + a4,所以etv[3] = 12。
function getEtvs(G){
let etvs = []; //用於儲存各頂點的最早發生時間
let stack = [], T = []; //stack為輔助棧,T為儲存拓撲序列的陣列
let count = 0; //用於統計頂點個數
for (let i=0;i<G.adjList.length;i++){ //初始化陣列,並將入度為0的頂點推入棧
etvs[i] = 0;
if (G.adjList[i].in === 0){
stack.push(i);
}
}
let currentAdjVex = null;
let currentIndex = 0;
while(stack.length > 0){
currentIndex = stack.pop(); //彈出棧頂入度為0的頂點
count++;
T.push(currentIndex);
currentAdjVex = G.adjList[currentIndex].firstEdge;
while(currentAdjVex){ //遍歷當前頂點的所有鄰接頂點
if (etvs[currentIndex] + currentAdjVex.weight > etvs[currentAdjVex.node]){ //關鍵程式碼,求各頂點事件最早發生時間
etvs[currentAdjVex.node] = etvs[currentIndex] + currentAdjVex.weight;
}
if (--G.adjList[currentAdjVex.node].in === 0){ //將當前鄰接頂點入度減1,若等於0,則推入棧中
stack.push(currentAdjVex.node);
}
currentAdjVex = currentAdjVex.next;
}
}
if (count < G.adjList.length){
return false;
}else{
return [etvs,T];
}
}
接著根據各頂點事件的最早發生時間,反向求各頂點的最晚發生時間,相當於把拓撲排序倒過來,判定條件如下
例如下圖,頂點v6的最晚發生時間為25,而頂點v7為19,ltv[6] - 9 > ltv[7] -4,為了保證工程不延期,因此頂點v4最晚發生時間ltv[4] = 15
function getLtvs(G,etvs,T){
let ltvs = [];
for (let i=0;i<G.adjList.length;i++){ //初始化每個頂點的最晚發生時間
ltvs[i] = etvs[G.adjList.length-1];
}
let currentAdjVex = null;
let currentIndex = 0;
while(T.length > 0){
currentIndex = T.pop(); //反向拓撲序列計算
currentAdjVex = G.adjList[currentIndex].firstEdge;
while(currentAdjVex){
if (ltvs[currentIndex] > ltvs[currentAdjVex.node] - currentAdjVex.weight){ //關鍵程式碼,求各頂點事件的最晚發生時間
ltvs[currentIndex] = ltvs[currentAdjVex.node] - currentAdjVex.weight;
}
currentAdjVex = currentAdjVex.next;
}
}
return ltvs;
}
最後即可根據各頂點事件的最早和最晚發生時間,計算弧上活動的最早和最晚時間,並作比較判斷是否為關鍵活動。注意的是,弧上活動<vi,vj>
的最早開工時間不可能早於頂點vi
事件最早發生時間,而最晚開工時間則不可能晚於頂點vj
事件最晚發生時間 - 弧上活動持續時間。因此程式碼如下:
function criticalPath(G){
let etvs = null, ltvs = null;
let T = null;
[etvs,T] = getEtvs(G); //呼叫函式獲取圖各頂點事件的最早和最晚發生時間
ltvs = getLtvs(G, etvs, T);
let ete = 0, lte = 0;
let currentAdjVex = null;
for (let i=0;i<G.adjList.length;i++){ //遍歷每一條邊
currentAdjVex = G.adjList[i].firstEdge;
while(currentAdjVex){
ete = etvs[i]; //弧上活動的最早開工時間
lte = ltvs[currentAdjVex.node] - currentAdjVex.weight; //弧上活動的最晚開工時間
if (ete === lte){
console.log('v'+i,'v'+currentAdjVex.node,currentAdjVex.weight);
}
currentAdjVex = currentAdjVex.next;
}
}
}
相關文章
- 優化關鍵渲染路徑優化
- 分散式系統關鍵路徑延遲分析實踐分散式
- 業界新突破 | 揭示自動化篩選關鍵告警實現路徑之一
- VOL.2 拓撲排序與關鍵路徑排序
- React.js中JSX的原理與關鍵實現ReactJS
- 改變固有思維的一種模式-關鍵路徑模式
- 智慧金融的破局(下):完成旅程的關鍵是路徑
- Pig 實現關鍵詞匹配
- 前端不止:Web效能優化–關鍵渲染路徑以及優化策略前端Web優化
- 說說你對瀏覽器的關鍵渲染路徑的理解瀏覽器
- JavaFx 關鍵字高亮文字實現Java
- 實現 MongoDB 外來鍵關聯MongoDB
- 基於路徑的實體圖關係抽取模型模型
- Require.js中的路徑在IDEA中的最佳實踐UIJSIdea
- js關鍵字和方法JS
- 解決pdf.js路徑問題JS
- “整裝化”發展未來:典型路徑與關鍵策略(附下載)
- 實現VR直播的關鍵技術VR
- 數字自然資源領域的實現路徑
- 應用內路徑規劃的簡單實現
- 【網路安全】PostMessage:分析JS實現XSSJS
- 軟體專案管理 7.4.2.進度計劃編排-關鍵路徑法專案管理
- 最佳實踐:路徑路由匹配規則的設計與實現路由
- canvas 實現光線沿不規則路徑運動Canvas
- 教育的數字化路徑:實現更大影響力
- js 如何提取富文字里的圖片路徑JS
- [原譯]實現IEnumerable介面&理解yield關鍵字
- 實現更好DEVOPS,關鍵取決於資料dev
- JavaScript new 關鍵詞解析及原生實現 newJavaScript
- 詳解智慧運維一體化建設實現路徑運維
- Feign通過自定義註解實現路徑的轉義
- Mac拷貝/複製資料夾路徑快捷鍵Mac
- vue 關於圖片路徑的問題Vue
- css檔案與資源路徑相關CSS
- 獲取當前js檔案被引用的路徑JS
- 「零信任」實施路徑探討
- 實時渲染路徑追蹤概述
- 【python】百度關鍵詞排名查詢實現Python