网络计划技术——PERT计划评审技术精解
计划评审技术(PERT,全称Program Evaluation and Review Technique)是一种用于项目管理的网络分析工具,旨在通过科学分析和评估活动的持续时间,合理分配资源、评估风险并优化项目进度安排。它特别关注工作中的不确定性,通过三点估算法(乐观时间、最可能时间、悲观时间)来估算工期,并对项目进展进行更准确的预测和调整。这种技术有助于项目经理全面考虑每项工作的执行者、资源需求、潜在风险以及是否能够在预定的时间内完成任务。当然项目经理在给出任务时间估算时,必须充分考虑不确定性和风险,PERT通过这种系统化的分析,不仅帮助项目经理提升计划的精确度和执行力,还能大幅度提高项目整体的管理效率。
作业明细表 | 网络计划图 |
---|---|
一、PERT技术概述
计划评审技术(Program Evaluation and Review Technique,简称PERT)是20世纪50年代末美国海军为“北极星导弹”项目开发的一种项目管理工具。它是一种用于规划和控制项目进度的网络模型方法,特别适合于具有不确定性的项目,通过分析任务之间的关系,估算任务的时间,并预测项目的总体进展情况。PERT的核心是将项目活动以网络图形式呈现,每个节点代表一个活动,每条路径代表任务之间的依赖关系,从而有效地管理和优化项目的进度计划。PERT的最大特点是能够处理不确定性和风险。传统的甘特图(Gantt Chart)虽然直观且易于理解,但它假设所有任务时间是确定的。而PERT则引入了三种时间估计方法,即乐观时间、最可能时间和悲观时间。通过这三种时间的权衡,PERT能帮助项目管理者更好地掌握任务的可能持续时间,并据此进行合理的项目进度安排。
1.1 PERT的核心原理
-
三点估算法:PERT的三点估算法是其核心,通过对每个任务的乐观时间(optimistic time, a)、最可能时间(most likely time, m)和悲观时间(pessimistic time, b)进行加权平均,从而估算任务的预期时间。具体公式为:$$T = \frac{a + 4m + b}{6}$$
该公式通过赋予最可能时间更大的权重,来估算项目中每个任务的平均时间。与此同时,PERT还计算出每项任务的方差(variance): $$\sigma^2 = \left(\frac{b - a}{6}\right)^2$$
方差的大小直接反映出任务时间的不确定性。方差越大,意味着任务存在的风险和不确定性也越大,反之则越小。 -
关键路径法(Critical Path Method, CPM):在PERT网络中,关键路径是指从项目开始到结束,所有必须完成的活动构成的路径中持续时间最长的一条。这条路径决定了项目的最短完工时间。若关键路径上的任何任务出现延误,整个项目的完工时间也将受到影响。因此,识别和优化关键路径是项目管理中的重要环节。PERT与CPM紧密相关,常常被结合使用。
-
风险分析:PERT通过方差计算,将项目进度中的风险量化。这对于不确定性较大的项目尤为重要。通过对不同任务的方差分析,项目经理可以提前识别出高风险任务,并采取相应的预防措施,确保项目按时完成。
1.2 PERT的发展与应用
PERT最早在军事领域获得应用,尤其是在美国海军的“北极星导弹”项目中,它为高度复杂的系统工程项目提供了科学的进度管理方法。由于这一项目涉及到大量不确定因素、复杂的任务依赖和长时间的开发周期,传统的管理工具显得无能为力。而PERT通过引入三点估算法,帮助项目管理者更好地估算了任务的持续时间并管理项目进度,最终显著加快了“北极星导弹”的开发进程。随着时间的推移,PERT逐渐被推广到各个领域,尤其是那些复杂性高、存在大量不确定因素的项目,如航空航天、建筑、软件开发、科研项目等。在现代项目管理中,PERT常常与关键路径法(CPM)、甘特图等工具结合使用,形成一套更加全面、灵活的项目管理工具。在商业领域,PERT被广泛应用于新产品开发(NPD)和研究与开发(R&D)项目。由于这些项目通常涉及创新、市场不确定性和技术挑战,使用PERT可以帮助企业更好地控制项目进度,并合理分配资源。与此同时,随着项目管理软件的普及,PERT图被集成到如Microsoft Project等项目管理工具中,使得项目管理者能够更加直观、快捷地进行任务分解和时间估算。
PERT的优势 | PERT的局限性 |
---|---|
处理不确定性:与传统的时间估算方法不同,PERT允许项目管理者根据乐观、最可能和悲观的估算来权衡项目中的不确定性。通过考虑任务时间的方差,管理者可以更清晰地识别项目中的风险点,并做出相应的调整。 | PERT假设项目中的任务能够被明确分解,并且它们之间的依赖关系是清晰的。然而,在一些高度创新性或研发项目中,任务的依赖关系往往不是固定的,项目进度也可能随着开发过程中的新发现或变化而发生调整。 |
科学的时间估算:三点估算法通过加权平均为项目中的每项任务提供了更为科学的时间估算。相比单一估算,这种方法更加符合实际情况,因为它能够考虑不同时间估算中的不确定性。 | PERT的时间估算依赖于专家的判断,因此其准确性很大程度上取决于这些估算的准确性。如果项目团队对任务的乐观、最可能和悲观时间的估算不够精确,PERT的估算结果也将偏离实际情况。 |
全局视角:通过网络图形式呈现任务的依赖关系和时间进度,PERT帮助项目经理从全局视角来管理项目。它不仅能显示每项任务的时间估算,还能直观地显示任务之间的依赖性和关键路径,帮助管理者更好地规划和优化项目进度。 | |
风险量化:通过对每项任务的方差计算,PERT将项目中的不确定性和风险量化,便于项目管理者进行风险管理。识别出高风险任务后,管理者可以优先关注这些任务,确保项目按时完成。 |
二、关键路线的Python计算
2.1 案例1
就下面工序明细表,给出关键路线,并计算关键路线的期望和方差
工序 | A | B | C | D | E | F | G | H | I |
---|---|---|---|---|---|---|---|---|---|
紧前工序 | - | A | A | B, C | A | B, E | C | F, G | H |
乐观时间 (a) / 天 | 2 | 6 | 6 | 1 | 8 | 5 | 3 | 3 | 5 |
最可能时间 (m) / 天 | 5 | 9 | 7 | 4 | 8 | 14 | 12 | 6 | 8 |
悲观时间 (b) / 天 | 8 | 12 | 8 | 7 | 8 | 17 | 15 | 9 | 11 |
# 工序数据定义
tasks_data = {
'A': {'a': 2, 'm': 5, 'b': 8, 'predecessors': []},
'B': {'a': 6, 'm': 9, 'b': 12, 'predecessors': ['A']},
'C': {'a': 6, 'm': 7, 'b': 8, 'predecessors': ['A']},
'D': {'a': 1, 'm': 4, 'b': 7, 'predecessors': ['B', 'C']},
'E': {'a': 8, 'm': 8, 'b': 8, 'predecessors': ['A']},
'F': {'a': 5, 'm': 14, 'b': 17, 'predecessors': ['B', 'E']},
'G': {'a': 3, 'm': 12, 'b': 15, 'predecessors': ['C']},
'H': {'a': 3, 'm': 6, 'b': 9, 'predecessors': ['F', 'G']},
'I': {'a': 5, 'm': 8, 'b': 11, 'predecessors': ['H']}
}
# 计算期望时间和方差的函数
def calculate_expectation_and_variance(task):
a = task['a']
m = task['m']
b = task['b']
expected_time = round((a + 4 * m + b) / 6, 2)
variance = round(((b - a) / 6) ** 2, 2)
return expected_time, variance
# 前向遍历:计算最早开始和最早结束时间,并找到关键路径
def forward_pass(task_name, tasks_data, memo={}):
if task_name in memo:
return memo[task_name]
task = tasks_data[task_name]
if not task['predecessors']:
return task['E'], task['Var'], [task_name]
max_time = 0
total_variance = 0
critical_path = []
# 遍历所有前置工序,选取时间最长的路径
for predecessor in task['predecessors']:
pred_time, pred_variance, pred_path = forward_pass(predecessor, tasks_data, memo)
# 选择时间最长的路径,并考虑方差
if pred_time > max_time:
max_time = pred_time
total_variance = pred_variance
critical_path = pred_path
# 记录计算结果,更新最早完成时间并保存到memo
memo[task_name] = (task['E'] + max_time, task['Var'] + total_variance, critical_path + [task_name])
return memo[task_name]
# 计算所有任务的期望时间和方差
for task_name, task_data in tasks_data.items():
expected_time, variance = calculate_expectation_and_variance(task_data)
tasks_data[task_name]['E'] = expected_time
tasks_data[task_name]['Var'] = variance
# 从最后一个工序开始找到关键路径(假设'I'是最后的工序)
critical_path_time, critical_path_variance, critical_path = forward_pass('I', tasks_data)
# 输出计算结果
print("工序的期望时间和方差:")
for task_name, task_data in tasks_data.items():
print(f"工序 {task_name}: 期望时间 = {task_data['E']:.2f} 天, 方差 = {task_data['Var']:.2f}")
print("\n关键路径总工期和方差:")
print(f"关键路径总工期 = {critical_path_time:.2f} 天")
print(f"关键路径方差 = {critical_path_variance:.2f}")
print("\n关键路径上的工序:")
print(" -> ".join(critical_path))
工序的期望时间和方差:
工序 A: 期望时间 = 5.00 天, 方差 = 1.00
工序 B: 期望时间 = 9.00 天, 方差 = 1.00
工序 C: 期望时间 = 7.00 天, 方差 = 0.11
工序 D: 期望时间 = 4.00 天, 方差 = 1.00
工序 E: 期望时间 = 8.00 天, 方差 = 0.00
工序 F: 期望时间 = 13.00 天, 方差 = 4.00
工序 G: 期望时间 = 11.00 天, 方差 = 4.00
工序 H: 期望时间 = 6.00 天, 方差 = 1.00
工序 I: 期望时间 = 8.00 天, 方差 = 1.00
关键路径总工期和方差:
关键路径总工期 = 41.00 天
关键路径方差 = 8.00
关键路径上的工序:
A -> B -> F -> H -> I
2.2 案例2
就下面工序明细表,给出关键路线,并计算关键路线的期望和方差
工序 | 紧前工序 | \(a\) | \(m\) | \(b\) | $$\frac{a+4m+b}{6}$$ | $$\sigma ^2 = (\frac{b-a}{6})^2$$ |
---|---|---|---|---|---|---|
A | 无 | 30 | 45 | 60 | 45 | 25 |
B | 无 | 15 | 20 | 30 | 21 | 6.25 |
C | A、B | 10 | 14 | 20 | 15 | 2.78 |
D | C | 30 | 45 | 60 | 45 | 25 |
E | D | 20 | 25 | 40 | 27 | 11.11 |
F | D | 90 | 180 | 240 | 175 | 625 |
G | D | 30 | 50 | 80 | 52 | 69.44 |
H | G | 10 | 14 | 21 | 15 | 3.36 |
I | H | 30 | 50 | 60 | 49 | 25 |
J | H | 30 | 38 | 45 | 38 | 6.25 |
K | E、F、I、J | 20 | 30 | 45 | 31 | 17.36 |
L | K | 30 | 60 | 120 | 65 | 225 |
M | K | 20 | 40 | 60 | 40 | 44.44 |
N | M | 10 | 14 | 20 | 15 | 2.78 |
# 工序数据定义
tasks_data = {
'A': {'a': 30, 'm': 45, 'b': 60, 'predecessors': []},
'B': {'a': 15, 'm': 20, 'b': 30, 'predecessors': []},
'C': {'a': 10, 'm': 14, 'b': 20, 'predecessors': ['A', 'B']},
'D': {'a': 30, 'm': 45, 'b': 60, 'predecessors': ['C']},
'E': {'a': 20, 'm': 25, 'b': 40, 'predecessors': ['D']},
'F': {'a': 90, 'm': 180, 'b': 240, 'predecessors': ['D']},
'G': {'a': 30, 'm': 50, 'b': 80, 'predecessors': ['D']},
'H': {'a': 10, 'm': 14, 'b': 21, 'predecessors': ['G']},
'I': {'a': 30, 'm': 50, 'b': 60, 'predecessors': ['H']},
'J': {'a': 30, 'm': 38, 'b': 45, 'predecessors': ['H']},
'K': {'a': 20, 'm': 30, 'b': 45, 'predecessors': ['E', 'F', 'I', 'J']},
'L': {'a': 30, 'm': 60, 'b': 120, 'predecessors': ['K']},
'M': {'a': 20, 'm': 40, 'b': 60, 'predecessors': ['K']},
'N': {'a': 10, 'm': 14, 'b': 20, 'predecessors': ['L']}
}
# 计算期望时间和方差的函数
def calculate_expectation_and_variance(task):
a = task['a']
m = task['m']
b = task['b']
expected_time = round((a + 4 * m + b) / 6, 2)
variance = round(((b - a) / 6) ** 2, 2)
return expected_time, variance
# 前向遍历:计算最早开始和最早结束时间,并找到关键路径
def forward_pass(task_name, tasks_data, memo={}):
if task_name in memo:
return memo[task_name]
task = tasks_data[task_name]
if not task['predecessors']:
return task['E'], task['Var'], [task_name]
max_time = 0
total_variance = 0
critical_path = []
# 遍历所有前置工序,选取时间最长的路径
for predecessor in task['predecessors']:
pred_time, pred_variance, pred_path = forward_pass(predecessor, tasks_data, memo)
# 选择时间最长的路径,并考虑方差
if pred_time > max_time:
max_time = pred_time
total_variance = pred_variance
critical_path = pred_path
# 记录计算结果,更新最早完成时间并保存到memo
memo[task_name] = (task['E'] + max_time, task['Var'] + total_variance, critical_path + [task_name])
return memo[task_name]
# 计算所有任务的期望时间和方差
for task_name, task_data in tasks_data.items():
expected_time, variance = calculate_expectation_and_variance(task_data)
tasks_data[task_name]['E'] = expected_time
tasks_data[task_name]['Var'] = variance
# 从最后一个工序开始找到关键路径(假设'N'是最后的工序)
critical_path_time, critical_path_variance, critical_path = forward_pass('N', tasks_data)
# 输出计算结果
print("工序的期望时间和方差:")
for task_name, task_data in tasks_data.items():
print(f"工序 {task_name}: 期望时间 = {task_data['E']:.2f} 天, 方差 = {task_data['Var']:.2f}")
print("\n关键路径总工期和方差:")
print(f"关键路径总工期 = {critical_path_time:.2f} 天")
print(f"关键路径方差 = {critical_path_variance:.2f}")
print("\n关键路径上的工序:")
print(" -> ".join(critical_path))
工序的期望时间和方差:
工序 A: 期望时间 = 45.00 天, 方差 = 25.00
工序 B: 期望时间 = 20.83 天, 方差 = 6.25
工序 C: 期望时间 = 14.33 天, 方差 = 2.78
工序 D: 期望时间 = 45.00 天, 方差 = 25.00
工序 E: 期望时间 = 26.67 天, 方差 = 11.11
工序 F: 期望时间 = 175.00 天, 方差 = 625.00
工序 G: 期望时间 = 51.67 天, 方差 = 69.44
工序 H: 期望时间 = 14.50 天, 方差 = 3.36
工序 I: 期望时间 = 48.33 天, 方差 = 25.00
工序 J: 期望时间 = 37.83 天, 方差 = 6.25
工序 K: 期望时间 = 30.83 天, 方差 = 17.36
工序 L: 期望时间 = 65.00 天, 方差 = 225.00
工序 M: 期望时间 = 40.00 天, 方差 = 44.44
工序 N: 期望时间 = 14.33 天, 方差 = 2.78
关键路径总工期和方差:
关键路径总工期 = 389.49 天
关键路径方差 = 922.92
关键路径上的工序:
A -> C -> D -> F -> K -> L -> N
三、网络计划的优化
在产品开发过程中,关键路径分析(Critical Path Method, CPM)是一种有效的管理工具,能够帮助项目管理人员识别对项目工期影响最大的关键流程。通过对关键路径的合理优化,企业可以在不增加资源投入的前提下,最大限度地缩短产品开发周期,降低开发成本。以下是三种常见的优化措施:
并行工程(Concurrent Engineering):并行工程是一种通过并行执行多个流程来加快项目进度的有效策略。在传统的项目开发过程中,不同工序往往是串行执行的,即一个任务完成后,另一个任务才能开始。然而,通过将关键流程分解为几个并行进行的子流程,或者让多个流程交叉作业,项目可以显著缩短整体工期。例如,在新产品的设计和测试阶段,企业可以同时进行设计优化和原型测试,从而减少等待时间。并行工程的实施需要项目各环节之间良好的协作,以避免资源冲突和信息沟通不畅。
压缩关键流程时间:关键路径上的流程决定了项目的总工期,因此压缩关键流程的时间是缩短项目周期的核心手段。为了缩短关键路径时间,项目团队可以在这些流程中引入新技术、改进工艺、升级设备,或者优化工作流程。同时,企业应确保关键路径上的资源得到优先配置,包括人力、物力、财力和精力的投入。如果非关键路径流程和关键路径流程之间发生冲突,企业应优先保障关键流程的资源需求,避免对项目总工期造成影响。通过这些手段,关键流程的时间可以得到有效压缩,从而加快项目整体进度。
挖掘非关键流程的潜力:非关键路径流程虽然不直接影响项目的总工期,但仍然存在优化的空间。利用非关键流程的时差进行合理调度,可以将一部分资源从非关键流程中转移到关键流程上。例如,抽调非关键流程中的人力和物力,支援关键流程的执行,能够有效缩短关键工序的时间。此外,企业还可以通过更灵活的计划安排和更高效的资源利用,最大限度地发挥非关键流程的潜力,避免浪费时间和资源。
总结
计划评审技术(PERT)是一种强大的项目管理工具,尤其适用于那些任务复杂、充满不确定性的项目。它不仅能通过三点估算法提供科学的时间估算,还能通过网络图形式帮助管理者更好地理解项目中的任务依赖关系和关键路径。然而,PERT的应用效果取决于项目团队对任务时间的准确估算,并且它需要与其他管理工具(如关键路径法和甘特图)结合使用,才能充分发挥其优势。在现代项目管理中,灵活应用PERT可以显著提高项目的进度控制和风险管理水平。