您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Python如何實(shí)現(xiàn)關(guān)鍵路徑和七格圖計(jì)算”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
主程序主要實(shí)現(xiàn)了一個(gè)Project類,其中包含了計(jì)算關(guān)鍵路徑和七格圖的方法。具體實(shí)現(xiàn)方式如下:
1. 定義了一個(gè)Activity類,包含了活動的id、名稱、持續(xù)時(shí)間和緊前任務(wù)列表等屬性。
2. 定義了一個(gè)Project類,包含了活動列表、項(xiàng)目持續(xù)時(shí)間、日志等屬性,以及計(jì)算關(guān)鍵路徑、計(jì)算七格圖、計(jì)算總浮動時(shí)間、計(jì)算自由浮動時(shí)間等方法。
3. 從JSON文件中讀取活動信息,并創(chuàng)建Project對象并添加活動。
4. 調(diào)用Project對象的calculate方法,計(jì)算每個(gè)活動的最早開始時(shí)間、最晚開始時(shí)間等數(shù)據(jù)。
5. 調(diào)用Project對象的calculate_critical_path方法,計(jì)算關(guān)鍵路徑。
6. 調(diào)用Project對象的calculate_project_duration方法,計(jì)算項(xiàng)目總工期。
7. 使用Jinja2模板引擎生成項(xiàng)目的活動清單,并將關(guān)鍵路徑
import json from datetime import datetime from typing import List import graphviz from jinja2 import Template from activity import Activity class Project: def __init__(self): self.activities: List[Activity] = [] self.duration = 0 self.logger = [] def log(self, log: str) -> None: self.logger.append(log) def add_activity(self, activity: Activity) -> None: """ 添加一個(gè)活動到項(xiàng)目中 :param activity: 待添加的活動 """ # 將活動添加到項(xiàng)目中 self.activities.append(activity) def calculate(self) -> None: """ 計(jì)算整個(gè)項(xiàng)目的關(guān)鍵信息 :return: None """ self.calculate_successor() self._calculate_forward_pass() # 計(jì)算正推法 self._calculate_backward_pass() # 計(jì)算倒推法 self._calculate_total_floats() # 計(jì)算總浮動時(shí)間 self._calculate_free_floats() # 計(jì)算自由浮動時(shí)間 def calculate_successor(self) -> None: self.log("開始計(jì)算緊后活動") for act in self.activities: for pred in act.predecessors: for act_inner in self.activities: if act_inner.id == pred: act_inner.successors.append(act.id) def _calculate_forward_pass(self) -> None: self.log("## 開始正推法計(jì)算") # 進(jìn)入 while 循環(huán),只有當(dāng)所有活動的最早開始時(shí)間和最早完成時(shí)間都已經(jīng)計(jì)算出來時(shí),才會退出循環(huán) while not self._is_forward_pass_calculated(): # 遍歷每個(gè)活動 for activity in self.activities: # 如果活動的最早開始時(shí)間已經(jīng)被計(jì)算過,則跳過 if activity.est is not None: continue # 如果活動沒有前置活動, 則從1開始計(jì)算最早開始時(shí)間和最早結(jié)束時(shí)間 if not activity.predecessors: activity.est = 1 activity.eft = activity.est + activity.duration - 1 self.log( f"活動 {activity.name} 沒有緊前活動,設(shè)定最早開始時(shí)間為1, 并根據(jù)工期計(jì)算最早結(jié)束時(shí)間為{activity.eft}") else: # 計(jì)算當(dāng)前活動的所有前置活動的最早完成時(shí)間 predecessors_eft = [act.eft for act in self.activities if act.id in activity.predecessors and act.eft is not None] # 如果當(dāng)前活動的所有前置活動的最早完成時(shí)間都已經(jīng)計(jì)算出來,則計(jì)算當(dāng)前活動的最早開始時(shí)間和最早完成時(shí)間 if len(predecessors_eft) == len(activity.predecessors): activity.est = max(predecessors_eft) + 1 activity.eft = activity.est + activity.duration - 1 self.log( f"活動 {activity.name} 緊前活動已完成正推法計(jì)算, 開始日期按最早開始時(shí)間里面最大的," + f"設(shè)定為{activity.est}并根據(jù)工期計(jì)算最早結(jié)束時(shí)間為{activity.eft}") # 更新項(xiàng)目總持續(xù)時(shí)間為最大最早完成時(shí)間 self.duration = max([act.eft for act in self.activities]) def _calculate_backward_pass(self) -> None: """ 計(jì)算倒推法 :return: None """ self.log("## 開始倒推法計(jì)算") # 輸出提示信息 # 進(jìn)入 while 循環(huán),只有當(dāng)所有活動的最晚開始時(shí)間和最晚完成時(shí)間都已經(jīng)計(jì)算出來時(shí),才會退出循環(huán) while not self._is_backward_pass_calculated(): # 遍歷每個(gè)活動 for act in reversed(self.activities): # 如果活動的最晚開始時(shí)間已經(jīng)被計(jì)算過,則跳過 if act.lft is not None: continue # 如果活動沒有后繼活動, 則從總持續(xù)時(shí)間開始計(jì)算最晚開始時(shí)間和最晚結(jié)束時(shí)間 if not act.successors: act.lft = self.duration act.lst = act.lft - act.duration + 1 self.log(f"活動 {act.name} 沒有緊后活動,按照正推工期設(shè)定最晚結(jié)束時(shí)間為{act.lft}," + f"并根據(jù)工期計(jì)算最晚開始時(shí)間為{act.lst}") else: # 計(jì)算當(dāng)前活動的所有后繼活動的最晚開始時(shí)間 successors_lst = self._calculate_lst(act) # 如果當(dāng)前活動的所有后繼活動的最晚開始時(shí)間都已經(jīng)計(jì)算出來,則計(jì)算當(dāng)前活動的最晚開始時(shí)間和最晚完成時(shí)間 if len(successors_lst) == len(act.successors): act.lft = min(successors_lst) - 1 act.lst = act.lft - act.duration + 1 self.log(f"活動 {act.name} 緊后活動計(jì)算完成,按照倒推工期設(shè)定最晚結(jié)束時(shí)間為{act.lft}," + f"并根據(jù)工期計(jì)算最晚開始時(shí)間為{act.lst}") # 更新項(xiàng)目總持續(xù)時(shí)間為最大最晚完成時(shí)間 self.duration = max([act.lft for act in self.activities]) def _calculate_lst(self, activity: Activity) -> List[int]: """計(jì)算某一活動的所有最晚開始時(shí)間 :param activity: 活動對象 :return: 最晚開始時(shí)間列表 """ rst = [] # 初始化結(jié)果列表 for act in activity.successors: # 遍歷該活動的后繼活動 for act2 in self.activities: # 遍歷所有活動 if act2.id == act and act2.lst is not None: # 如果找到了該后繼活動且其最晚開始時(shí)間不為空 rst.append(act2.lst) # 將最晚開始時(shí)間加入結(jié)果列表 return rst # 返回結(jié)果列表 def _is_forward_pass_calculated(self) -> bool: """ 判斷整個(gè)項(xiàng)目正推法計(jì)算已經(jīng)完成 :return: 若已計(jì)算正向傳遞則返回True,否則返回False """ for act in self.activities: # 遍歷所有活動 if act.est is None or act.eft is None: # 如果該活動的最早開始時(shí)間或最早完成時(shí)間為空 return False # 則返回False,表示還未計(jì)算正向傳遞 return True # 如果所有活動的最早開始時(shí)間和最早完成時(shí)間都已計(jì)算,則返回True,表示已計(jì)算正向傳遞 def _is_backward_pass_calculated(self) -> bool: """ 判斷整個(gè)項(xiàng)目倒推法計(jì)算已經(jīng)完成 :return: 若已計(jì)算倒推法則返回True,否則返回False """ for act in self.activities: # 遍歷所有活動 if act.lst is None or act.lft is None: # 如果該活動的最晚開始時(shí)間或最晚完成時(shí)間為空 return False # 則返回False,表示還未計(jì)算倒推法 return True # 如果所有活動的最晚開始時(shí)間和最晚完成時(shí)間都已計(jì)算,則返回True,表示已計(jì)算倒推法 def _calculate_total_floats(self) -> None: """ 計(jì)算所有活動的總浮動時(shí)間 :return: None """ self.log(f"## 開始計(jì)算項(xiàng)目所有活動的總浮動時(shí)間") for act in self.activities: # 遍歷所有活動 if act.est is not None and act.lst is not None: # 如果該活動的最早開始時(shí)間和最晚開始時(shí)間都已計(jì)算 act.tf = act.lst - act.est # 則計(jì)算該活動的總浮動時(shí)間 self.log(f"計(jì)算{act.name}的總浮動時(shí)間" + f"最晚開始時(shí)間{act.lst} - 最早開始時(shí)間{act.est} = {act.tf}", ) else: # 如果該活動的最早開始時(shí)間或最晚開始時(shí)間為空 act.tf = None # 則將該活動的總浮動時(shí)間設(shè)為None def _calculate_free_floats(self) -> None: """ 計(jì)算所有活動的自由浮動時(shí)間 :return: None """ self.log(f"## 開始計(jì)算項(xiàng)目所有活動的自由浮動時(shí)間") # 輸出提示信息 for act in self.activities: # 遍歷所有活動 if act.tf == 0: # 如果該活動的總浮動時(shí)間為0 self.log(f"計(jì)算{act.name}的自由浮動時(shí)間" + f"因?yàn)閧act.name}的總浮動時(shí)間為0,自由浮動時(shí)間為0") # 輸出提示信息 act.ff = 0 # 則將該活動的自由浮動時(shí)間設(shè)為0 elif act.tf > 0: # 如果該活動的總浮動時(shí)間大于0 self.log(f"計(jì)算{act.name}的自由浮動時(shí)間") # 輸出提示信息 self.log(f"- {act.name}的總浮動時(shí)間{act.tf} > 0,") # 輸出提示信息 tmp = [] # 初始化臨時(shí)列表 for act2 in self.activities: # 遍歷所有活動 if act2.id in act.successors: # 如果該活動是該活動的緊后活動 self.log(f"- {act.name}的緊后活動{act2.name}的自由浮動動時(shí)間為{act2.tf}") # 輸出提示信息 tmp.append(act2.tf) # 將該緊后活動的自由浮動時(shí)間加入臨時(shí)列表 if len(tmp) != 0: # 如果臨時(shí)列表不為空 act.ff = act.tf - max(tmp) # 則計(jì)算該活動的自由浮動時(shí)間 if act.ff < 0: act.ff = 0 self.log(f"- 用活動自己的總浮動{act.tf}減去多個(gè)緊后活動總浮動的最大值{max(tmp)} = {act.ff}") else: # 如果臨時(shí)列表為空 act.ff = act.tf # 則將該活動的自由浮動時(shí)間設(shè)為總浮動時(shí)間 def calculate_critical_path(self) -> List[Activity]: """ 計(jì)算整個(gè)項(xiàng)目的關(guān)鍵路徑 :return: 整個(gè)項(xiàng)目的關(guān)鍵路徑 """ ctc_path = [] # 初始化關(guān)鍵路徑列表 for act in self.activities: # 遍歷所有活動 if act.tf == 0: # 如果該活動的總浮動時(shí)間為0 ctc_path.append(act) # 則將該活動加入關(guān)鍵路徑列表 return ctc_path # 返回關(guān)鍵路徑列表 def calculate_project_duration(self) -> int: """ 計(jì)算整個(gè)項(xiàng)目的持續(xù)時(shí)間 :return: 整個(gè)項(xiàng)目的持續(xù)時(shí)間 """ return max(activity.eft for activity in self.activities) # 返回所有活動的最早完成時(shí)間中的最大值,即整個(gè)項(xiàng)目的持續(xù)時(shí)間 # 從JSON文件中讀取活動信息 with open('activities.json', 'r', encoding='utf-8') as f: activities_data = json.load(f) # 創(chuàng)建Project對象并添加活動 project = Project() for activity_data in activities_data: activity = Activity( activity_data['id'], activity_data['name'], activity_data['duration'], activity_data['predecessors'] ) project.add_activity(activity) # 計(jì)算每個(gè)活動的最早開始時(shí)間、最晚開始時(shí)間等數(shù)據(jù) project.calculate() # 計(jì)算關(guān)鍵路徑和項(xiàng)目總工期 critical_path = project.calculate_critical_path() project_duration = project.calculate_project_duration() # 生成項(xiàng)目的活動清單 with open('template.html', 'r', encoding='utf-8') as f: template = Template(f.read()) html = template.render( activities=project.activities, critical_path=critical_path, project_duration=project_duration, log=project.logger ) # 生成項(xiàng)目進(jìn)度網(wǎng)絡(luò)圖 aon_graph = graphviz.Digraph(format='png', graph_attr={'rankdir': 'LR'}) for activity in project.activities: aon_graph.node(str(activity.id), activity.name) for predecessor in activity.predecessors: aon_graph.edge(str(predecessor), str(activity.id)) timestamp = datetime.now().strftime('%Y%m%d%H%M%S') aon_filename = f"aon_{timestamp}" aon_graph.render(aon_filename) # 將項(xiàng)目進(jìn)度網(wǎng)絡(luò)圖插入到HTML文件中 aon_image = f'<img src="{aon_filename}.png" alt="Precedence Diagramming Method: AON">' html = html.replace('<p>Precedence Diagramming Method: AON: <br/>[image]</p>', '<p>緊前關(guān)系繪圖法: AON: <br/>' + aon_image + '</p>') filename = datetime.now().strftime('%Y%m%d%H%M%S') + '.html' with open(filename, 'w', encoding='utf-8') as f: f.write(html)
程序名:activity.py
class Activity: """ 活動類,用于表示項(xiàng)目中的一個(gè)活動。 Attributes: id (int): 活動的唯一標(biāo)識符。 name (str): 活動的名稱。 duration (int): 活動的持續(xù)時(shí)間。 predecessors (List[int]): 活動的前置活動列表,存儲前置活動的id。 est (int): 活動的最早開始時(shí)間。 lst (int): 活動的最晚開始時(shí)間。 eft (int): 活動的最早完成時(shí)間。 lft (int): 活動的最晚完成時(shí)間。 tf (int): 活動的總浮動時(shí)間。 ff (int): 活動的自由浮動時(shí)間。 successors (List[int]): 活動的后繼活動列表,存儲后繼活動的Activity對象。 """ def __init__(self, id: int, name: str, duration: int, predecessors: List[int]): """ 初始化活動對象。 Args: id (int): 活動的唯一標(biāo)識符。 name (str): 活動的名稱。 duration (int): 活動的持續(xù)時(shí)間。 predecessors (List[int]): 活動的前置活動列表,存儲前置活動的id。 """ self.id = id self.name = name self.duration = duration self.predecessors = predecessors self.est = None self.lst = None self.eft = None self.lft = None self.tf = None self.ff = None self.successors = [] def __str__(self): return f"id: {self.id}, name: {self.name}, est: {self.est}, lst: {self.lst}, eft: {self.eft}, lft: {self.lft}," + f"successors: {self.successors}"
文件名:activities.json
[ { "id": 1, "name": "A", "duration": 2, "predecessors": [] }, { "id": 9, "name": "A2", "duration": 3, "predecessors": [] }, { "id": 10, "name": "A3", "duration": 2, "predecessors": [] }, { "id": 2, "name": "B", "duration": 3, "predecessors": [ 1, 9 ] }, { "id": 3, "name": "C", "duration": 4, "predecessors": [ 1 ] }, { "id": 4, "name": "D", "duration": 2, "predecessors": [ 2,10 ] }, { "id": 5, "name": "E", "duration": 3, "predecessors": [ 2 ] }, { "id": 6, "name": "F", "duration": 2, "predecessors": [ 3 ] }, { "id": 7, "name": "G", "duration": 3, "predecessors": [ 4, 5 ] }, { "id": 8, "name": "H", "duration": 2, "predecessors": [ 6, 7 ] }, { "id": 11, "name": "H2", "duration": 4, "predecessors": [ 6, 7 ] } ]
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>PMP關(guān)鍵路徑計(jì)算</title> <style> table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid black; padding: 8px; text-align: center; } th { background-color: #4CAF50; color: white; } .critical { background-color: #ffcccc; } </style> </head> <body> <h2>活動清單</h2> <table> <tr> <th>ID</th> <th>活動名</th> <th>持續(xù)時(shí)間</th> <th>緊前活動</th> <th>緊后活動</th> <th>最早開始時(shí)間EST</th> <th>最早結(jié)束時(shí)間EFT</th> <th>最晚開始時(shí)間LST</th> <th>最晚結(jié)束時(shí)間LFT</th> <th>總浮動時(shí)間TF</th> <th>自由浮動時(shí)間FF</th> </tr> {% for activity in activities %} <tr {% if activity in critical_path %}class="critical" {% endif %}> <td>{{ activity.id }}</td> <td>{{ activity.name }}</td> <td>{{ activity.duration }}</td> <td> {% for predecessor in activity.predecessors %} {% for act in activities %} {% if act.id == predecessor %} {{ act.name }} {% endif %} {% endfor %} {% if not loop.last %}, {% endif %} {% endfor %} </td> <td> {% for successor in activity.successors %} {% for act in activities %} {% if act.id == successor %} {{ act.name }} {% endif %} {% endfor %} {% if not loop.last %}, {% endif %} {% endfor %} </td> <td>{{ activity.est }}</td> <td>{{ activity.eft }}</td> <td>{{ activity.lst }}</td> <td>{{ activity.lft }}</td> <td>{{ activity.tf }}</td> <td>{{ activity.ff }}</td> </tr> {% endfor %} </table> <p>關(guān)鍵路徑是: {% for activity in critical_path %}{{ activity.name }}{% if not loop.last %} -> {% endif %}{% endfor %}</p> <p>項(xiàng)目總工期: {{ project_duration }}</p> <p>Precedence Diagramming Method: AON: <br/>[image]</p> <p> <table> <tr> <th>執(zhí)行過程</th> </tr> {% for i in log %} <tr> <td >{{i}}</td> </tr> {% endfor %} </table> </p> </body> </html>
“Python如何實(shí)現(xiàn)關(guān)鍵路徑和七格圖計(jì)算”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。