NetworkX系列教程(10)-算法之三:關鍵路徑問題


重頭戲部分來了,寫到這里我感覺得仔細認真點了,可能在NetworkX中,實現某些算法就一句話的事,但是這個算法是做什么的,用在什么地方,原理是怎么樣的,不清除,所以,我決定先把圖論中常用算法弄個明白在寫這部分.

圖論常用算法看我的博客:

下面我將使用NetworkX實現上面的算法,建議不清楚的部分打開兩篇博客對照理解.
我將圖論的經典問題及常用算法的總結寫在下面兩篇博客中:
圖論---問題篇
圖論---算法篇

目錄:
* 11.3關鍵路徑算法(CPA)


注意:如果代碼出現找不庫,請返回第一個教程,把庫文件導入.

11.3關鍵路徑算法(CPA)

以下代碼從這里復制,由於版本問題,將代碼中的:nx.topological_sort(self, reverse=True)改為list(reversed(list(nx.topological_sort(self))))

  1. import networkx as nx 
  2. import matplotlib.pyplot as plt 
  3. from matplotlib.font_manager import *  
  4.  
  5. #定義自定義字體,文件名從1.b查看系統中文字體中來  
  6. myfont = FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc')  
  7. #解決負號'-'顯示為方塊的問題  
  8. matplotlib.rcParams['axes.unicode_minus']=False  
  9.  
  10. class CPM(nx.DiGraph): 
  11.  
  12. def __init__(self): 
  13. super().__init__() 
  14. self._dirty = True 
  15. self._critical_path_length = -1 
  16. self._criticalPath = None 
  17.  
  18. def add_node(self, *args, **kwargs): 
  19. self._dirty = True 
  20. super().add_node(*args, **kwargs) 
  21.  
  22. def add_nodes_from(self, *args, **kwargs): 
  23. self._dirty = True 
  24. super().add_nodes_from(*args, **kwargs) 
  25.  
  26. def add_edge(self, *args): # , **kwargs): 
  27. self._dirty = True 
  28. super().add_edge(*args) # , **kwargs) 
  29.  
  30. def add_edges_from(self, *args, **kwargs): 
  31. self._dirty = True 
  32. super().add_edges_from(*args, **kwargs) 
  33.  
  34. def remove_node(self, *args, **kwargs): 
  35. self._dirty = True 
  36. super().remove_node(*args, **kwargs) 
  37.  
  38. def remove_nodes_from(self, *args, **kwargs): 
  39. self._dirty = True 
  40. super().remove_nodes_from(*args, **kwargs) 
  41.  
  42. def remove_edge(self, *args): # , **kwargs): 
  43. self._dirty = True 
  44. super().remove_edge(*args) # , **kwargs) 
  45.  
  46. def remove_edges_from(self, *args, **kwargs): 
  47. self._dirty = True 
  48. super().remove_edges_from(*args, **kwargs) 
  49.  
  50. #根據前向拓撲排序算弧的最早發生時間 
  51. def _forward(self): 
  52. for n in nx.topological_sort(self): 
  53. es = max([self.node[j]['EF'] for j in self.predecessors(n)], default=0) 
  54. self.add_node(n, ES=es, EF=es + self.node[n]['duration']) 
  55.  
  56. #根據前向拓撲排序算弧的最遲發生時間 
  57. def _backward(self): 
  58. #for n in nx.topological_sort(self, reverse=True): 
  59. for n in list(reversed(list(nx.topological_sort(self)))): 
  60. lf = min([self.node[j]['LS'] for j in self.successors(n)], default=self._critical_path_length) 
  61. self.add_node(n, LS=lf - self.node[n]['duration'], LF=lf) 
  62.  
  63. #最早發生時間=最遲發生時間,則判斷該節點為關鍵路徑上的關鍵活動 
  64. def _compute_critical_path(self): 
  65. graph = set() 
  66. for n in self: 
  67. if self.node[n]['EF'] == self.node[n]['LF']: 
  68. graph.add(n) 
  69. self._criticalPath = self.subgraph(graph) 
  70.  
  71. @property 
  72. def critical_path_length(self): 
  73. if self._dirty: 
  74. self._update() 
  75. return self._critical_path_length 
  76.  
  77. @property 
  78. def critical_path(self): 
  79. if self._dirty: 
  80. self._update() 
  81. return sorted(self._criticalPath, key=lambda x: self.node[x]['ES']) 
  82.  
  83. def _update(self): 
  84. self._forward() 
  85. self._critical_path_length = max(nx.get_node_attributes(self, 'EF').values()) 
  86. self._backward() 
  87. self._compute_critical_path() 
  88. self._dirty = False 
  89.  
  90. if __name__ == "__main__": 
  91.  
  92. #構建graph 
  93. G = CPM() 
  94. G.add_node('A', duration=5) 
  95. G.add_node('B', duration=2) 
  96. G.add_node('C', duration=4) 
  97. G.add_node('D', duration=4) 
  98. G.add_node('E', duration=3) 
  99. G.add_node('F', duration=7) 
  100. G.add_node('G', duration=4) 
  101.  
  102. G.add_edges_from([ 
  103. ('A', 'B'), 
  104. ('A', 'C'), 
  105. ('C','D'), 
  106. ('C','E'), 
  107. ('C','G'), 
  108. ('B','D'), 
  109. ('D','F'), 
  110. ('E','F'), 
  111. ('G','F'), 
  112. ])  
  113.  
  114. #顯示graph 
  115. nx.draw_spring(G,with_labels=True) 
  116. plt.title('AOE網絡',fontproperties=myfont) 
  117. plt.axis('on') 
  118. plt.xticks([]) 
  119. plt.yticks([]) 
  120. plt.show() 
  121.  
  122.  
  123. print('關鍵活動為:') 
  124. print(G.critical_path_length, G.critical_path) 
  125.  
  126. G.add_node('D', duration=2) 
  127. print('\n修改D活動持續時間4為2后的關鍵活動為:') 
  128. print(G.critical_path_length, G.critical_path) 

關鍵路徑示例(該圖非黑色線為手工繪制,數字手工添加)
關鍵路徑示例(該圖非黑色線為手工繪制,數字手工添加)

從graph中可以知道,有兩條關鍵路徑,分別是:A->C->G->FA->C->D->F,長度都是20.

輸出:

關鍵活動為: 20 ['A', 'C', 'D', 'G', 'F']

修改D活動持續時間4為2后的關鍵活動為: 20 ['A', 'C', 'G', 'F']

關鍵活動為: ['A', 'C', 'D', 'G', 'F'],可以構成兩條邊.D活動持續時間4為2后,關鍵路徑變化.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM