Python代码故障排查、优化及Matplotlib绘图改进求助
Phi近似程序问题排查、性能优化与绘图改进
问题描述
合并两个Phi值近似程序后,运行15分钟无输出,终止后报错。需排查代码问题、优化性能并改进Matplotlib绘图方案。
问题排查
1. 函数重复定义
代码中approximate_phi_progression被定义两次,第一次函数逻辑混乱且存在严重性能问题,第二次是正确的蒙特卡洛近似逻辑,但第一次定义会导致Method2的计算完全错误。
2. 递归函数栈溢出与计算量爆炸
第一次定义的函数中,onephi是递归函数,elif onephi() == 1会触发无限递归,极易导致栈溢出;同时嵌套循环层级过多:
- 外层100次trial循环
- 每个trial调用
approximate_phi_progression,内部循环10000次point - 每次point内又循环100次trial,每个trial再循环10000次调用
onephi
总计算量达10^12次操作,完全无法在合理时间内完成。
3. 变量作用域错误
第一次定义approximate_phi_progression时,提前使用了未赋值的num_trials和num_points_per_trial,会触发NameError。
4. 数据结构错误
Method2的phi_progression每次将整个phi_approximation列表追加进去,导致最终数据是多层嵌套列表,后续绘图时维度不匹配。
性能优化
1. 重构Method2逻辑,移除无效递归
将onephi改为非递归实现,避免栈溢出(原递归逻辑等价于生成符合黄金分割概率的0/1值,可直接推导公式简化):
def onephi(): true_phi_inv = (math.sqrt(5)-1)/2 # 黄金分割倒数≈0.618 return 1 if random.random() < true_phi_inv else 0
2. 简化嵌套循环,减少计算量
重新梳理Method2的计算流程,避免不必要的嵌套:
def approximate_phi_method2(num_trials_per_run, runs_per_trial): phi_progression = [] for _ in range(num_trials_per_run): c = sum(onephi() for _ in range(runs_per_trial)) phi = runs_per_trial / c if c != 0 else math.nan # 避免除以0 phi_progression.append(phi) return phi_progression # 生成Method2数据 method2_progressions = [approximate_phi_method2(num_trials, num_points_per_trial) for _ in range(num_trials)]
3. 使用numpy向量化加速
将循环替换为numpy数组操作,大幅提升计算速度:
import numpy as np def approximate_phi_method2_numpy(num_trials_per_run, runs_per_trial): phi_progression = [] true_phi_inv = (np.sqrt(5)-1)/2 for _ in range(num_trials_per_run): rand_vals = np.random.rand(runs_per_trial) c = np.sum(rand_vals < true_phi_inv) phi = runs_per_trial / c if c !=0 else np.nan phi_progression.append(phi) return phi_progression
4. 提前初始化变量与列表
避免在循环内重复定义函数、初始化列表,减少内存开销与动态扩容耗时。
Matplotlib绘图优化
1. 简化图例,避免重复标签
只在第一次绘制某类曲线时添加标签,后续绘制时省略:
plt.figure(figsize=(12, 6)) # 绘制Method1曲线 for idx, trial_progression in enumerate(method1_progressions): x = np.arange(num_trials, num_points_per_trial+1, num_trials) plt.plot(x, trial_progression, alpha=0.3, color="red", label='Method 1' if idx==0 else "") # 绘制Method2曲线 for idx, trial_progression in enumerate(method2_progressions): x = np.arange(1, num_trials+1) plt.plot(x, trial_progression, alpha=0.3, color="blue", label='Method 2' if idx==0 else "")
2. 添加真实Phi值参考线
让近似效果对比更直观:
true_phi = (1 + np.sqrt(5))/2 plt.axhline(y=true_phi, color='green', linestyle='--', label='True Phi')
3. 绘制统计趋势线
用均值±标准差展示整体近似效果,避免100条线过于杂乱:
# Method1统计量 method1_mean = np.mean(method1_progressions, axis=0) method1_std = np.std(method1_progressions, axis=0) x1 = np.arange(num_trials, num_points_per_trial+1, num_trials) plt.plot(x1, method1_mean, color='darkred', linewidth=2, label='Method 1 Mean') plt.fill_between(x1, method1_mean-method1_std, method1_mean+method1_std, color='red', alpha=0.1) # Method2统计量 method2_mean = np.mean(method2_progressions, axis=0) method2_std = np.std(method2_progressions, axis=0) x2 = np.arange(1, num_trials+1) plt.plot(x2, method2_mean, color='darkblue', linewidth=2, label='Method 2 Mean') plt.fill_between(x2, method2_mean-method2_std, method2_mean+method2_std, color='blue', alpha=0.1)
4. 优化图表样式
plt.title('Phi Value Approximation Comparison', fontsize=14) plt.xlabel('Number of Trials/Points', fontsize=12) plt.ylabel('Approximated Phi', fontsize=12) plt.legend(loc='upper right') plt.grid(alpha=0.3) plt.tight_layout() plt.show()
完整优化后代码
import random import math import numpy as np import matplotlib.pyplot as plt # Method1:基于圆的蒙特卡洛近似 def approximate_phi_progression(num_points, track_interval=100): inside_circle = 0 phi_progression = [] for point in range(1, num_points + 1): x = random.uniform(0, 1) y = random.uniform(0, 1) if x**2 + y**2 <= 1: inside_circle += 1 if point % track_interval == 0: phi_squared_approximation = (5/6) * (4 * inside_circle / point) phi_approximation = math.sqrt(phi_squared_approximation) phi_progression.append(phi_approximation) return phi_progression # Method2:基于黄金分割概率的向量化实现 def approximate_phi_method2_numpy(num_trials_per_run, runs_per_trial): phi_progression = [] true_phi_inv = (np.sqrt(5)-1)/2 # 黄金分割倒数≈0.618 for _ in range(num_trials_per_run): rand_vals = np.random.rand(runs_per_trial) c = np.sum(rand_vals < true_phi_inv) phi = runs_per_trial / c if c !=0 else np.nan phi_progression.append(phi) return phi_progression # 参数设置 num_trials = 100 num_points_per_trial = 10000 # 生成数据 method1_progressions = [approximate_phi_progression(num_points_per_trial) for _ in range(num_trials)] method2_progressions = [approximate_phi_method2_numpy(num_trials, num_points_per_trial) for _ in range(num_trials)] # 绘图 plt.figure(figsize=(12, 6)) true_phi = (1 + np.sqrt(5))/2 # Method1统计趋势 x1 = np.arange(num_trials, num_points_per_trial+1, num_trials) method1_mean = np.mean(method1_progressions, axis=0) method1_std = np.std(method1_progressions, axis=0) plt.plot(x1, method1_mean, color='darkred', linewidth=2, label='Method 1 Mean') plt.fill_between(x1, method1_mean-method1_std, method1_mean+method1_std, color='red', alpha=0.1) # Method2统计趋势 x2 = np.arange(1, num_trials+1) method2_mean = np.mean(method2_progressions, axis=0) method2_std = np.std(method2_progressions, axis=0) plt.plot(x2, method2_mean, color='darkblue', linewidth=2, label='Method 2 Mean') plt.fill_between(x2, method2_mean-method2_std, method2_mean+method2_std, color='blue', alpha=0.1) # 添加真实Phi参考线 plt.axhline(y=true_phi, color='green', linestyle='--', label='True Phi') # 图表样式设置 plt.title('Phi Value Approximation Comparison', fontsize=14) plt.xlabel('Number of Trials/Points', fontsize=12) plt.ylabel('Approximated Phi', fontsize=12) plt.legend(loc='upper right') plt.grid(alpha=0.3) plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者user23380519
相关产品推荐
相关产品推荐

