图解评审法 (GERT) 实战:基于 Python 模拟求解项目工期与成本期望值
图解评审法 (GERT) 实战基于 Python 模拟求解项目工期与成本期望值在项目管理领域传统的关键路径法CPM和计划评审技术PERT已经无法完全应对现代项目中的复杂性和不确定性。图解评审法GERT作为一种更高级的网络分析技术能够有效处理带有概率分支和反馈回路的随机网络问题。本文将带您从理论走向实践通过Python编程实现GERT网络的自动化计算与可视化分析。1. GERT网络基础与核心概念GERT网络与传统网络计划技术的本质区别在于其引入了概率分支和反馈回路的概念。在GERT网络中一个活动的完成可能导致多种不同的结果每种结果都有其发生的概率和相应的参数变化。GERT网络的三大核心要素节点类型输入节点仅允许活动进入的节点输出节点仅允许活动离开的节点混合节点同时允许活动进入和离开的节点活动参数实现概率p活动成功完成的概率时间参数t活动持续时间通常表示为随机变量成本参数c活动消耗成本也可表示为随机变量网络特性允许循环和反馈支持多入口多出口结构能够处理条件分支提示GERT网络特别适合研发项目、创新工程等存在高度不确定性的场景其中活动的成功与否往往会影响后续路径的选择。2. Python实现GERT模拟的核心算法要实现GERT网络的模拟计算我们需要构建几个关键算法模块。下面将详细介绍每个模块的实现方法。2.1 网络表示与数据结构我们首先定义网络的数据结构使用邻接表表示法存储网络拓扑class GERTNetwork: def __init__(self): self.nodes {} # 节点字典 {node_id: Node} self.edges [] # 边列表 [(from, to, p, t_dist, c_dist)] def add_node(self, node_id, node_type): self.nodes[node_id] {type: node_type, edges_out: []} def add_edge(self, from_node, to_node, probability, time_dist, cost_dist): self.edges.append((from_node, to_node, probability, time_dist, cost_dist)) self.nodes[from_node][edges_out].append(len(self.edges)-1)2.2 Mason公式的实现Mason公式是计算GERT网络参数的核心数学工具。以下是其Python实现def mason_formula(network, start_node, end_node): # 计算从start_node到end_node的传输函数 paths find_all_paths(network, start_node, end_node) loops find_all_loops(network) # 计算Δ值系统行列式 delta 1 for loop in loops: delta - loop[transmission] # 计算各路径的传输值与对应的余因子 result 0 for path in paths: path_trans 1 for edge in path[edges]: path_trans * network.edges[edge][2] # 概率相乘 # 计算不与该路径接触的回路 non_touching 1 for loop in loops: if not path_loop_intersect(path, loop): non_touching - loop[transmission] result path_trans * non_touching return result / delta2.3 蒙特卡洛模拟方法对于复杂的GERT网络我们可以采用蒙特卡洛模拟来估算节点实现概率和期望值def monte_carlo_simulation(network, start_node, end_node, num_simulations10000): success_count 0 total_time 0 total_cost 0 for _ in range(num_simulations): current_node start_node path_time 0 path_cost 0 visited set() while True: if current_node end_node: success_count 1 total_time path_time total_cost path_cost break if current_node in visited: break # 避免无限循环 visited.add(current_node) # 获取当前节点的出边 out_edges network.nodes[current_node][edges_out] if not out_edges: break # 根据概率选择一条边 probs [network.edges[e][2] for e in out_edges] chosen_edge np.random.choice(out_edges, pprobs/np.sum(probs)) # 累加时间和成本 path_time sample_from_dist(network.edges[chosen_edge][3]) path_cost sample_from_dist(network.edges[chosen_edge][4]) current_node network.edges[chosen_edge][1] realization_prob success_count / num_simulations expected_time total_time / success_count if success_count 0 else float(inf) expected_cost total_cost / success_count if success_count 0 else float(inf) return realization_prob, expected_time, expected_cost3. 完整案例新产品研发项目模拟让我们通过一个具体案例来演示如何使用Python实现GERT网络分析。假设我们有一个新产品研发项目其GERT网络结构如下3.1 项目网络构建首先定义项目网络结构和参数# 创建GERT网络实例 project GERTNetwork() # 添加节点 nodes [ (1, start), # 项目开始 (2, normal), # 初步设计 (3, normal), # 原型开发 (4, normal), # 测试验证 (5, normal), # 设计修改 (6, normal), # 重新开发 (7, normal), # 最终测试 (8, end) # 项目完成 ] for node_id, node_type in nodes: project.add_node(node_id, node_type) # 添加边活动 edges [ # from, to, 概率, 时间分布, 成本分布 (1, 2, 1.0, (norm, 10, 2), (norm, 5000, 1000)), (2, 3, 0.8, (norm, 20, 3), (norm, 15000, 2000)), (3, 4, 0.7, (norm, 15, 2), (norm, 10000, 1500)), (4, 8, 0.6, (norm, 5, 1), (norm, 3000, 500)), # 一次成功 (4, 5, 0.4, (norm, 8, 1), (norm, 5000, 800)), # 需要修改 (5, 6, 1.0, (norm, 10, 2), (norm, 8000, 1200)), (6, 7, 1.0, (norm, 12, 2), (norm, 7000, 1000)), (7, 8, 0.9, (norm, 7, 1), (norm, 4000, 600)), # 最终成功 (7, 5, 0.1, (norm, 5, 1), (norm, 3000, 500)) # 需要再次修改 ] for edge in edges: project.add_edge(*edge)3.2 分析与可视化我们可以使用networkx和matplotlib库来可视化网络结构import networkx as nx import matplotlib.pyplot as plt def visualize_network(network): G nx.DiGraph() # 添加节点 for node_id in network.nodes: G.add_node(node_id) # 添加边 for i, (from_node, to_node, prob, t_dist, c_dist) in enumerate(network.edges): label fp{prob}\nt{t_dist[1]}±{t_dist[2]}\nc{c_dist[1]}±{c_dist[2]} G.add_edge(from_node, to_node, labellabel, weightprob) # 绘制 pos nx.spring_layout(G) plt.figure(figsize(12, 8)) nx.draw(G, pos, with_labelsTrue, node_size2000, node_colorskyblue) edge_labels nx.get_edge_attributes(G, label) nx.draw_networkx_edge_labels(G, pos, edge_labelsedge_labels) plt.title(GERT Network Visualization) plt.show() visualize_network(project)3.3 模拟结果分析运行蒙特卡洛模拟并分析结果# 运行模拟 prob, time, cost monte_carlo_simulation(project, 1, 8, 100000) print(f项目成功概率: {prob:.2%}) print(f期望工期: {time:.1f} 天) print(f期望成本: ${cost/1000:.1f}k) # 输出结果示例 # 项目成功概率: 98.76% # 期望工期: 52.3 天 # 期望成本: $45.2k为了更深入地理解项目风险我们可以分析模拟结果的分布def analyze_simulation(network, num_simulations10000): times [] costs [] successes 0 for _ in range(num_simulations): # 运行单次模拟... # 记录成功案例的时间和成本 if success: times.append(time) costs.append(cost) successes 1 # 绘制分布图 plt.figure(figsize(12, 5)) plt.subplot(1, 2, 1) plt.hist(times, bins30, colorblue, alpha0.7) plt.title(工期分布) plt.xlabel(天数) plt.subplot(1, 2, 2) plt.hist(costs, bins30, colorgreen, alpha0.7) plt.title(成本分布) plt.xlabel(成本千元) plt.tight_layout() plt.show() return { success_rate: successes / num_simulations, time_mean: np.mean(times), time_std: np.std(times), cost_mean: np.mean(costs), cost_std: np.std(costs) } stats analyze_simulation(project)4. 高级应用与优化技巧掌握了基本实现后我们可以进一步优化GERT网络分析的方法和效率。4.1 敏感性分析识别对项目结果影响最大的活动def sensitivity_analysis(network, target_node, n_runs1000): base_prob, base_time, base_cost monte_carlo_simulation(network, 1, target_node, n_runs) results [] for i, edge in enumerate(network.edges): # 备份原始概率 original_prob edge[2] # 修改概率增加10% modified_edges list(network.edges) modified_edges[i] (edge[0], edge[1], min(1.0, edge[2]*1.1), edge[3], edge[4]) # 创建修改后的网络 modified_network copy.deepcopy(network) modified_network.edges modified_edges # 重新模拟 new_prob, new_time, new_cost monte_carlo_simulation(modified_network, 1, target_node, n_runs) # 计算变化率 delta_prob (new_prob - base_prob) / base_prob delta_time (new_time - base_time) / base_time delta_cost (new_cost - base_cost) / base_cost results.append({ edge: i, from: edge[0], to: edge[1], delta_prob: delta_prob, delta_time: delta_time, delta_cost: delta_cost }) # 找出影响最大的活动 most_sensitive sorted(results, keylambda x: abs(x[delta_time]), reverseTrue)[0] return most_sensitive4.2 并行计算加速对于大型网络我们可以使用多进程加速模拟from multiprocessing import Pool def parallel_monte_carlo(args): network, start, end, n args return monte_carlo_simulation(network, start, end, n) def fast_simulation(network, start_node, end_node, total_simulations100000, n_workers4): chunk_size total_simulations // n_workers args [(network, start_node, end_node, chunk_size)] * n_workers with Pool(n_workers) as p: results p.map(parallel_monte_carlo, args) total_success sum(r[0] for r in results) total_time sum(r[1]*r[0] for r in results) total_cost sum(r[2]*r[0] for r in results) prob total_success / (chunk_size * n_workers) time total_time / total_success if total_success 0 else float(inf) cost total_cost / total_success if total_success 0 else float(inf) return prob, time, cost4.3 实时监控与动态调整在实际项目中我们可以实现动态GERT网络根据项目实际进展调整网络参数class DynamicGERTNetwork(GERTNetwork): def __init__(self): super().__init__() self.observed_data [] def update_based_on_observation(self, from_node, to_node, actual_time, actual_cost): self.observed_data.append((from_node, to_node, actual_time, actual_cost)) # 根据观察数据调整后续活动的分布参数 for i, edge in enumerate(self.edges): if edge[0] from_node and edge[1] to_node: # 简单示例调整时间分布的均值 old_dist edge[3] new_mean (old_dist[1] actual_time) / 2 new_dist (old_dist[0], new_mean, old_dist[2]) # 更新边 self.edges[i] (edge[0], edge[1], edge[2], new_dist, edge[4]) break def get_current_recommendation(self, current_node): # 根据当前状态和更新后的网络推荐最佳下一步行动 out_edges self.nodes[current_node][edges_out] if not out_edges: return None # 简单策略选择期望时间最短的路径 best_edge None min_expected_time float(inf) for edge_idx in out_edges: edge self.edges[edge_idx] prob edge[2] t_mean edge[3][1] # 递归估算从目标节点到终点的期望时间 _, sub_time, _ monte_carlo_simulation(self, edge[1], 8, 1000) total_expected t_mean sub_time if total_expected min_expected_time: min_expected_time total_expected best_edge edge_idx return best_edge