Sublime Text运行Python代码遇卡顿及math domain error问题求助
解决Simpson法则串行/并行对比代码的报错与卡顿问题
问题分析
1. ValueError: math domain error 原因
报错触发于math.acos()函数,该函数要求参数必须在**[-1, 1]**范围内,原代码的核心问题有两个:
- 多进程全局变量同步失效:
g函数依赖全局变量h,但multiprocessing.Pool创建的子进程会复制主进程初始内存空间,后续主进程循环中更新的h值无法同步到子进程,导致g函数始终使用初始值h=π/2,计算出的x值远超积分区间[0, π/2],使得math.cos(x)/(1+2*math.cos(x))的绝对值可能大于1,触发定义域错误。 - 浮点计算边界风险:即使
x在合法区间,极端情况下的浮点误差也可能导致表达式结果轻微超出[-1,1]范围。
2. 构建卡顿原因
原循环中i的范围是1到9,当i=9时n=10^9,串行部分需要执行5亿次循环,计算量远超常规设备承载能力,必然导致卡顿甚至无响应。
修复后的代码
import math import numpy as np import time import multiprocessing as mp import matplotlib.pyplot as plt from functools import partial def f(x): y = math.cos(x) val = y / (1 + 2 * y) # 钳位处理,确保参数在acos合法范围内 val = max(min(val, 1.0), -1.0) return math.acos(val) b = math.pi / 2 a = 0 exact = math.pi ** 2 / 16 # 改写g函数,通过参数传递h,避免全局变量依赖 def g(j, h): return (f(a + (2 * j - 2) * h) + 4 * f(a + (2 * j - 1) * h) + f(a + (2 * j) * h)) if __name__ == '__main__': mp.freeze_support() # Windows系统必需 # 缩小循环范围,避免计算量过大 max_power = 6 serial_times = [] parallel_times = [] intervals = [] # 每次循环重新创建进程池(或用starmap传递多参数) for i in range(1, max_power + 1): n = 10 ** i h = (b - a) / n N = int(n / 2) intervals.append(N) # 并行计算:用partial绑定h参数 with mp.Pool(processes=mp.cpu_count()) as pool: start_time = time.time() g_with_h = partial(g, h=h) p = pool.map(g_with_h, np.linspace(1, N, N, dtype=np.int32)) P = (h / 3) * np.sum(p, dtype=np.float64) elapsedTime = time.time() - start_time parallel_times.append(elapsedTime) # 串行计算 start_time = time.time() S = 0.0 for j in range(1, N+1): S += f(a + (2 * j - 2) * h) + 4 * f(a + (2 * j - 1) * h) + f(a + (2 * j) * h) S *= h / 3 elapsedTime = time.time() - start_time serial_times.append(elapsedTime) print(f"完成N={N}的计算,串行耗时{elapsedTime:.4f}s,并行耗时{parallel_times[-1]:.4f}s") # 绘图 plt.plot(intervals, serial_times, label='串行计算', marker='o') plt.plot(intervals, parallel_times, label='并行计算', marker='s') plt.xlabel('区间数量(N)') plt.ylabel('运行时间(s)') plt.xscale('log') plt.yscale('log') plt.title('Simpson法则计算时间 vs 区间数量') plt.legend() plt.grid(True, which="both", ls="-", alpha=0.5) plt.show()
关键修复点说明
- 移除全局变量依赖:用
functools.partial将h绑定到g函数,确保子进程使用当前循环的h值,解决多进程参数同步问题。 - 添加钳位处理:在
f函数中对表达式结果做max(min(val, 1.0), -1.0)处理,强制将参数限制在acos的合法定义域内。 - 控制计算规模:将循环上限从9改为6,避免极端大的计算量导致卡顿;同时添加打印语句,实时查看计算进度。
- 优化进程池使用:采用
with语句管理进程池,确保每次循环后正确关闭进程,避免资源泄漏。
运行效果
修复后代码可正常执行,会输出每个区间数量下的串行/并行耗时,并生成对比曲线图,清晰展示并行计算在大区间数量下的时间优势。
内容的提问来源于stack exchange,提问作者FIG
相关产品推荐
相关产品推荐

