为何我的Python圆周率计算器运行速度随时间逐渐变慢?
问题:Galperin圆周率模拟程序运行速度急剧下降
我最近看了一段讲解Galperin圆周率计算方法的视频,照着做了个Python模拟程序。程序逻辑没问题,但运行速度越来越慢:一开始每秒能算几百次碰撞,很快降到40-50次,跑久了甚至只剩2次每秒。
以下是我的代码:
# imports from time import time, sleep from fractions import Fraction # set variables p = [Fraction(8, 1), Fraction(10, 1)] m = [1, 1] v = [Fraction(0, 1), Fraction(-1, 1)] collisions = 0 time_interval = 1 # main print("The π Calculator\n\nCredits:\nOriginal formula discovered by Gregory Galperin\nSimulation by Te Du\n") sleep(1) m[1] = 100 ** int(input("How many digits would you like to calculate? ")) start_time = time() prev_time = start_time while not (v[0] <= v[1] and v[0] >= 0): if time() - prev_time >= time_interval: try: print("Seconds elapsed: " + str(time() - start_time) + "\nCollisions: " + str(collisions)) except ValueError: prev_time = time() + 1000000000 time_interval = 2 - (time() - prev_time) prev_time = time() try: wall_to_one = p[0] / -v[0] except ZeroDivisionError: wall_to_one = 0 one_to_two = (p[1] - p[0]) / (v[0] - v[1]) collisions += 1 if one_to_two <= 0 or (0 < wall_to_one and wall_to_one < one_to_two): p[0] += v[0] * wall_to_one p[1] += v[1] * wall_to_one v[0] *= -1 else: p[0] += v[0] * one_to_two p[1] += v[1] * one_to_two v = [Fraction(m[0] - m[1], m[1] + m[0]) * v[0] + Fraction(2 * m[1], m[1] + m[0]) * v[1], Fraction(2 * m[0], m[1] + m[0]) * v[0] + Fraction(m[1] - m[0], m[1] + m[0]) * v[1]] # cleanup del p, m, v, wall_to_one, one_to_two, start_time, prev_time, time_interval # printing pi print("π: " + str(collisions))
问题根源
fractions.Fraction的性能损耗:随着碰撞次数增加,分数的分子分母会指数级膨胀,每次运算都需要计算大整数的最大公约数(GCD),这会导致运算时间急剧上升。- 不合理的性能监控逻辑:
time_interval = 2 - (time() - prev_time)会让打印间隔越来越小,频繁的printIO操作进一步消耗CPU资源。
优化方案
1. 替换Fraction为浮点数(牺牲精度换速度)
浮点数运算为硬件原生支持,速度远快于大整数分数运算,对于10位以内的圆周率计算完全足够:
# 变量初始化改为浮点数 p = [8.0, 10.0] m = [1, 1] v = [0.0, -1.0] ... # 碰撞速度计算简化 total_mass = m[0] + m[1] v0_new = (m[0] - m[1])/total_mass * v[0] + 2*m[1]/total_mass * v[1] v1_new = 2*m[0]/total_mass * v[0] + (m[1] - m[0])/total_mass * v[1] v = [v0_new, v1_new]
2. 优化性能监控逻辑
固定打印间隔,减少IO操作,用f-string提升拼接效率:
print_interval = 1 # 每秒打印一次 start_time = time() prev_time = start_time while not (v[0] <= v[1] and v[0] >= 0): current_time = time() if current_time - prev_time >= print_interval: print(f"Seconds elapsed: {current_time - start_time:.2f}\nCollisions: {collisions}") prev_time = current_time ...
3. 整数镜像模拟(高精度高效方案)
如果需要精确计算更多位数,可利用Galperin模型的对称性,将碰撞转化为镜像反射,全程用整数运算避免分数,彻底消除GCD计算的性能损耗。
优化后代码示例(浮点数版本)
from time import time, sleep # set variables p = [8.0, 10.0] m = [1, 1] v = [0.0, -1.0] collisions = 0 print_interval = 1 # main print("The π Calculator\n\nCredits:\nOriginal formula discovered by Gregory Galperin\nSimulation by Te Du\n") sleep(1) digits = int(input("How many digits would you like to calculate? ")) m[1] = 100 ** digits start_time = time() prev_time = start_time while not (v[0] <= v[1] and v[0] >= 0): current_time = time() if current_time - prev_time >= print_interval: print(f"Seconds elapsed: {current_time - start_time:.2f}\nCollisions: {collisions}") prev_time = current_time try: wall_to_one = p[0] / -v[0] except ZeroDivisionError: wall_to_one = 0 one_to_two = (p[1] - p[0]) / (v[0] - v[1]) collisions += 1 if one_to_two <= 0 or (0 < wall_to_one and wall_to_one < one_to_two): # 撞墙 p[0] += v[0] * wall_to_one p[1] += v[1] * wall_to_one v[0] *= -1 else: # 两球相撞 p[0] += v[0] * one_to_two p[1] += v[1] * one_to_two total_mass = m[0] + m[1] v0_new = (m[0] - m[1])/total_mass * v[0] + 2*m[1]/total_mass * v[1] v1_new = 2*m[0]/total_mass * v[0] + (m[1] - m[0])/total_mass * v[1] v = [v0_new, v1_new] # 输出结果 print(f"π: {collisions}")
内容的提问来源于stack exchange,提问作者meowdog011011
相关产品推荐
相关产品推荐

