搜尋
首頁後端開發Python教學Python如何實現關鍵路徑與七格圖計算

1.主程式

主程式主要實作了一個Project類,其中包含了計算關鍵路徑和七格圖的方法。具體實現方式如下:

1.  定義了一個Activity類,包含了活動的id、名稱、持續時間和緊前任務清單等屬性。 

2.  定義了一個Project類,包含了活動清單、專案持續時間、日誌等屬性,以及計算關鍵路徑、計算七格圖、計算總浮動時間、計算自由浮動時間等方法。

3.  從JSON檔案中讀取活動訊息,並建立Project物件並新增活動。

4.  呼叫Project物件的calculate方法,計算每個活動的最早開始時間、最晚開始時間等資料。

5.  呼叫Project物件的calculate_critical_path方法,計算關鍵路徑。

6.  呼叫Project物件的calculate_project_duration方法,計算專案總工期。

7.  使用Jinja2範本引擎產生專案的活動清單,並將關鍵路徑

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:  
        """  
        添加一个活动到项目中  
        :param  
        activity: 待添加的活动  
        """        # 将活动添加到项目中  
        self.activities.append(activity)  
  
    def calculate(self) -> None:  
        """ 计算整个项目的关键信息  
        :return: None  
        """        self.calculate_successor()  
        self._calculate_forward_pass()  # 计算正推法  
        self._calculate_backward_pass()  # 计算倒推法  
        self._calculate_total_floats()  # 计算总浮动时间  
        self._calculate_free_floats()  # 计算自由浮动时间  
  
    def calculate_successor(self) -> None:  
        self.log("开始计算紧后活动")  
        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("## 开始正推法计算")  
        # 进入 while 循环,只有当所有活动的最早开始时间和最早完成时间都已经计算出来时,才会退出循环  
        while not self._is_forward_pass_calculated():  
            # 遍历每个活动  
            for activity in self.activities:  
                # 如果活动的最早开始时间已经被计算过,则跳过  
                if activity.est is not None:  
                    continue  
                # 如果活动没有前置活动, 则从1开始计算最早开始时间和最早结束时间  
                if not activity.predecessors:  
                    activity.est = 1  
                    activity.eft = activity.est + activity.duration - 1  
                    self.log(  
                        f"活动 {activity.name} 没有紧前活动,设定最早开始时间为1, 并根据工期计算最早结束时间为{activity.eft}")  
                else:  
                    # 计算当前活动的所有前置活动的最早完成时间  
                    predecessors_eft = [act.eft for act in self.activities if  
                                        act.id in activity.predecessors and act.eft is not None]  
                    # 如果当前活动的所有前置活动的最早完成时间都已经计算出来,则计算当前活动的最早开始时间和最早完成时间  
                    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} 紧前活动已完成正推法计算, 开始日期按最早开始时间里面最大的," +  
                            f"设定为{activity.est}并根据工期计算最早结束时间为{activity.eft}")  
  
                        # 更新项目总持续时间为最大最早完成时间  
        self.duration = max([act.eft for act in self.activities])  
  
    def _calculate_backward_pass(self) -> None:  
        """ 计算倒推法  
        :return: None  
        """  
        self.log("## 开始倒推法计算")  # 输出提示信息  
        # 进入 while 循环,只有当所有活动的最晚开始时间和最晚完成时间都已经计算出来时,才会退出循环  
        while not self._is_backward_pass_calculated():  
            # 遍历每个活动  
            for act in reversed(self.activities):  
                # 如果活动的最晚开始时间已经被计算过,则跳过  
                if act.lft is not None:  
                    continue  
                # 如果活动没有后继活动, 则从总持续时间开始计算最晚开始时间和最晚结束时间  
                if not act.successors:  
                    act.lft = self.duration  
                    act.lst = act.lft - act.duration + 1  
                    self.log(f"活动 {act.name} 没有紧后活动,按照正推工期设定最晚结束时间为{act.lft}," +  
                             f"并根据工期计算最晚开始时间为{act.lst}")  
                else:  
                    # 计算当前活动的所有后继活动的最晚开始时间  
                    successors_lst = self._calculate_lst(act)  
                    # 如果当前活动的所有后继活动的最晚开始时间都已经计算出来,则计算当前活动的最晚开始时间和最晚完成时间  
                    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} 紧后活动计算完成,按照倒推工期设定最晚结束时间为{act.lft}," +  
                                 f"并根据工期计算最晚开始时间为{act.lst}")  
        # 更新项目总持续时间为最大最晚完成时间  
        self.duration = max([act.lft for act in self.activities])  
  
    def _calculate_lst(self, activity: Activity) -> List[int]:  
        """计算某一活动的所有最晚开始时间  
        :param activity: 活动对象  
        :return: 最晚开始时间列表  
        """        rst = []  # 初始化结果列表  
        for act in activity.successors:  # 遍历该活动的后继活动  
            for act2 in self.activities:  # 遍历所有活动  
                if act2.id == act and act2.lst is not None:  # 如果找到了该后继活动且其最晚开始时间不为空  
                    rst.append(act2.lst)  # 将最晚开始时间加入结果列表  
        return rst  # 返回结果列表  
  
    def _is_forward_pass_calculated(self) -> bool:  
        """ 判断整个项目正推法计算已经完成  
        :return: 若已计算正向传递则返回True,否则返回False  
        """  
        for act in self.activities:  # 遍历所有活动  
            if act.est is None or act.eft is None:  # 如果该活动的最早开始时间或最早完成时间为空  
                return False  # 则返回False,表示还未计算正向传递  
        return True  # 如果所有活动的最早开始时间和最早完成时间都已计算,则返回True,表示已计算正向传递  
  
    def _is_backward_pass_calculated(self) -> bool:  
        """ 判断整个项目倒推法计算已经完成  
        :return: 若已计算倒推法则返回True,否则返回False  
        """        for act in self.activities:  # 遍历所有活动  
            if act.lst is None or act.lft is None:  # 如果该活动的最晚开始时间或最晚完成时间为空  
                return False  # 则返回False,表示还未计算倒推法  
        return True  # 如果所有活动的最晚开始时间和最晚完成时间都已计算,则返回True,表示已计算倒推法  
  
    def _calculate_total_floats(self) -> None:  
        """ 计算所有活动的总浮动时间  
        :return: None  
         """  
        self.log(f"## 开始计算项目所有活动的总浮动时间")  
        for act in self.activities:  # 遍历所有活动  
            if act.est is not None and act.lst is not None:  # 如果该活动的最早开始时间和最晚开始时间都已计算  
                act.tf = act.lst - act.est  # 则计算该活动的总浮动时间  
                self.log(f"计算{act.name}的总浮动时间" + f"最晚开始时间{act.lst} - 最早开始时间{act.est} = {act.tf}", )  
            else:  # 如果该活动的最早开始时间或最晚开始时间为空  
                act.tf = None  # 则将该活动的总浮动时间设为None  
  
    def _calculate_free_floats(self) -> None:  
        """ 计算所有活动的自由浮动时间  
        :return: None  
        """        self.log(f"## 开始计算项目所有活动的自由浮动时间")  # 输出提示信息  
        for act in self.activities:  # 遍历所有活动  
            if act.tf == 0:  # 如果该活动的总浮动时间为0  
                self.log(f"计算{act.name}的自由浮动时间" + f"因为{act.name}的总浮动时间为0,自由浮动时间为0")  # 输出提示信息  
                act.ff = 0  # 则将该活动的自由浮动时间设为0  
            elif act.tf > 0:  # 如果该活动的总浮动时间大于0  
                self.log(f"计算{act.name}的自由浮动时间")  # 输出提示信息  
                self.log(f"- {act.name}的总浮动时间{act.tf} > 0,")  # 输出提示信息  
                tmp = []  # 初始化临时列表  
                for act2 in self.activities:  # 遍历所有活动  
                    if act2.id in act.successors:  # 如果该活动是该活动的紧后活动  
                        self.log(f"- {act.name}的紧后活动{act2.name}的自由浮动动时间为{act2.tf}")  # 输出提示信息  
                        tmp.append(act2.tf)  # 将该紧后活动的自由浮动时间加入临时列表  
                if len(tmp) != 0:  # 如果临时列表不为空  
                    act.ff = act.tf - max(tmp)  # 则计算该活动的自由浮动时间  
                    if act.ff < 0:  
                        act.ff = 0  
                    self.log(f"- 用活动自己的总浮动{act.tf}减去多个紧后活动总浮动的最大值{max(tmp)} = {act.ff}")  
                else:  # 如果临时列表为空  
                    act.ff = act.tf  # 则将该活动的自由浮动时间设为总浮动时间  
  
    def calculate_critical_path(self) -> List[Activity]:  
        """ 计算整个项目的关键路径  
        :return: 整个项目的关键路径  
        """        ctc_path = []  # 初始化关键路径列表  
        for act in self.activities:  # 遍历所有活动  
            if act.tf == 0:  # 如果该活动的总浮动时间为0  
                ctc_path.append(act)  # 则将该活动加入关键路径列表  
        return ctc_path  # 返回关键路径列表  
  
    def calculate_project_duration(self) -> int:  
        """ 计算整个项目的持续时间  
        :return: 整个项目的持续时间  
        """        return max(activity.eft for activity in self.activities)  # 返回所有活动的最早完成时间中的最大值,即整个项目的持续时间  
  
  
# 从JSON文件中读取活动信息  
with open(&#39;activities.json&#39;, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f:  
    activities_data = json.load(f)  
  
# 创建Project对象并添加活动  
project = Project()  
for activity_data in activities_data:  
    activity = Activity(  
        activity_data[&#39;id&#39;],  
        activity_data[&#39;name&#39;],  
        activity_data[&#39;duration&#39;],  
        activity_data[&#39;predecessors&#39;]  
    )    project.add_activity(activity)  
  
# 计算每个活动的最早开始时间、最晚开始时间等数据  
project.calculate()  
  
# 计算关键路径和项目总工期  
critical_path = project.calculate_critical_path()  
project_duration = project.calculate_project_duration()  
  
# 生成项目的活动清单  
with open(&#39;template.html&#39;, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f:  
    template = Template(f.read())  
html = template.render(  
    activities=project.activities,  
    critical_path=critical_path,  
    project_duration=project_duration,  
    log=project.logger  
)  
  
# 生成项目进度网络图  
aon_graph = graphviz.Digraph(format=&#39;png&#39;, graph_attr={&#39;rankdir&#39;: &#39;LR&#39;})  
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(&#39;%Y%m%d%H%M%S&#39;)  
  
aon_filename = f"aon_{timestamp}"  
  
aon_graph.render(aon_filename)  
  
# 将项目进度网络图插入到HTML文件中  
aon_image = f&#39;<img src="/static/imghwm/default1.png"  data-src="{aon_filename}.png"  class="lazy"   alt="Precedence Diagramming Method: AON">&#39;  
html = html.replace(&#39;<p>Precedence Diagramming Method: AON: <br/>[image]</p>&#39;,  
                    &#39;<p>紧前关系绘图法: AON: <br/>&#39; + aon_image + &#39;</p>&#39;)  
  
filename = datetime.now().strftime(&#39;%Y%m%d%H%M%S&#39;) + &#39;.html&#39;  
with open(filename, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f:  
    f.write(html)

2.活動類別

程式名稱:activity.py

class Activity:  
    """  
    活动类,用于表示项目中的一个活动。  
  
    Attributes:        id (int): 活动的唯一标识符。  
        name (str): 活动的名称。  
        duration (int): 活动的持续时间。  
        predecessors (List[int]): 活动的前置活动列表,存储前置活动的id。  
        est (int): 活动的最早开始时间。  
        lst (int): 活动的最晚开始时间。  
        eft (int): 活动的最早完成时间。  
        lft (int): 活动的最晚完成时间。  
        tf (int): 活动的总浮动时间。  
        ff (int): 活动的自由浮动时间。  
        successors (List[int]): 活动的后继活动列表,存储后继活动的Activity对象。  
    """  
    def __init__(self, id: int, name: str, duration: int, predecessors: List[int]):  
        """  
        初始化活动对象。  
  
        Args:            id (int): 活动的唯一标识符。  
            name (str): 活动的名称。  
            duration (int): 活动的持续时间。  
            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}"

3.任務清單JSON檔案

檔名: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  
    ]  
  }  
]

4.輸出範本檔案

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="UTF-8">  
    <title>PMP关键路径计算</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 id="活动清单">活动清单</h2>  
<table>  
    <tr>  
        <th>ID</th>  
        <th>活动名</th>  
        <th>持续时间</th>  
        <th>紧前活动</th>  
        <th>紧后活动</th>  
        <th>最早开始时间EST</th>  
        <th>最早结束时间EFT</th>  
        <th>最晚开始时间LST</th>  
        <th>最晚结束时间LFT</th>  
        <th>总浮动时间TF</th>  
        <th>自由浮动时间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>关键路径是: {% for activity in critical_path %}{{ activity.name }}{% if not loop.last %} -> {% endif %}{% endfor  
    %}</p>  
<p>项目总工期: {{ project_duration }}</p>  
<p>Precedence Diagramming Method: AON: <br/>[image]</p>  
<p>  
<table>  
    <tr>  
        <th>执行过程</th>  
    </tr>  
    {% for i in log %}  
    <tr>  
        <td >{{i}}</td>  
    </tr>  
    {% endfor %}  
</table>  
</p>  
</body>  
</html>

以上是Python如何實現關鍵路徑與七格圖計算的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:亿速云。如有侵權,請聯絡admin@php.cn刪除
Python:自動化,腳本和任務管理Python:自動化,腳本和任務管理Apr 16, 2025 am 12:14 AM

Python在自動化、腳本編寫和任務管理中表現出色。 1)自動化:通過標準庫如os、shutil實現文件備份。 2)腳本編寫:使用psutil庫監控系統資源。 3)任務管理:利用schedule庫調度任務。 Python的易用性和豐富庫支持使其在這些領域中成為首選工具。

Python和時間:充分利用您的學習時間Python和時間:充分利用您的學習時間Apr 14, 2025 am 12:02 AM

要在有限的時間內最大化學習Python的效率,可以使用Python的datetime、time和schedule模塊。 1.datetime模塊用於記錄和規劃學習時間。 2.time模塊幫助設置學習和休息時間。 3.schedule模塊自動化安排每週學習任務。

Python:遊戲,Guis等Python:遊戲,Guis等Apr 13, 2025 am 12:14 AM

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

Python vs.C:申請和用例Python vs.C:申請和用例Apr 12, 2025 am 12:01 AM

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時的Python計劃:一種現實的方法2小時的Python計劃:一種現實的方法Apr 11, 2025 am 12:04 AM

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python:探索其主要應用程序Python:探索其主要應用程序Apr 10, 2025 am 09:41 AM

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

您可以在2小時內學到多少python?您可以在2小時內學到多少python?Apr 09, 2025 pm 04:33 PM

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?Apr 02, 2025 am 07:18 AM

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),