SheepChef Blog
何時までも夢へ求めてる
SheepChef Blog

用Python计算圆周率 π

各位读者想必对圆周率并不陌生——小学五年级,诸位就能接触到圆周率。

圆周率的简史

圆周率的几何定义十分简单,用圆的周长除以直径,就得到了圆周率。

但纯靠徒手测量,难免有些误差。古代的很多文明都有发现所谓“密率”,比如阿基米德率先提出 π 的大小应该在3.14到3.15之间,在算数并不发达的古希腊文明,将圆周率精确到两位小数已经是壮举。

汉代,中国人刘徽在《九章算术》中发明了“割圆法”,即对圆作外切和内接正N边形,不断逼近。他最终逼近到了正36边型,得到“徽率” π = 3.14,精确到两位小数。

祖冲之则继承了刘徽的算法,暴力计算到正24576边形,算出 π = 3.1415926,领先全世界几百年。

欧洲人在文艺复兴之后从中国人手中接走了红旗,达芬奇,欧拉,欧几里得等一众学者的出现让欧洲人发现了许多以更高效率计算 π 的方法,在电子计算机被发明之后,人们大可以想算几位就算几位。

计算 π 任意位数的精确值

公式

现在,我们通常用计算机循环迭代计算无穷级数,以此不断逼近 π 的值。为了提高效率,又要求这个无穷级数的收敛速度尽可能的快,这样我们就能以最少的计算量,算出最多的位数。一个常用的公式是马青公式,如下图。

马青公式由英国天文学教授约翰·马青于1706年发现。每计算一项就能将π的精确度提升1.4位小数。

现在公式摆在了面前,我们要设计一个Python脚本来实现它。

算法

在Python中,典型双精度浮点的精度上限不过几十位小数,如果我们要计算超过100位的圆周率,就不能简单地用变量和循环,这里需要引入 Decimal 库来实现十进制高精度计算。我们可以指定计算精度。我们还引入tqdm库,这样方便显示计算进度。

from decimal import *
from tqdm import tqdm

getcontext().prec = 100000 #十万位精度,需要9万余次迭代。

观察公式,不难发现公式大致分为两个部分,即那两个需要我们用循环迭代运算的无穷级数。这里把前面那项简称为前项,后面那项简称为后项,方便理解。

我们要先设定两项的初始值,分别是1/5和1/239。在式子中我们还能观察到式子中两项之间的符号在交替改变,所以设变量Flip来判断循环时该加上还是该减去。最后初始化Pi的变量,以及创建一个计次变量用于循环计次。

Pi = Decimal(0)
First = Decimal(1) / Decimal(5)
Last = Decimal(1) / Decimal(239)
Flip = False
Count = Decimal(1)

Acc = 95000 #迭代次数,每一次迭代可以推进1.4十进制位的精度。

接下来进入主要的循环运算部分

for i in tqdm(range(Acc),position=0,file=sys.stdout,desc="Progress"):
	if Count != Decimal(1):
		if not Flip:
			First = First - (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(5)**(Decimal(2)*Count - Decimal(1)))))
			Last = Last - (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(239)**(Decimal(2)*Count - Decimal(1)))))
			Flip = not Flip
		elif Flip:
			First = First + (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(5)**(Decimal(2)*Count - Decimal(1)))))
			Last = Last + (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(239)**(Decimal(2)*Count - Decimal(1)))))
			Flip = not Flip
		Pi = (Decimal(16) * First) - (Decimal(4) * Last)
	Count = Count + Decimal(1)
	if Count % 100 == 0 or Count == Acc - 1:
		#os.system('cls')  # 清空Windows控制台
		#tqdm.write(str(Pi))
		pass
Python

为了确保计算精度,所有的运算操作全部都要用Decimal对象进行,如果在此期间不进行任何控制台输出,将显著提高运行速度,尤其在精度特别高的时候。

最终的运算结果就是变量Pi的值,直接输出即可。

计算效率

Python是执行速度相对较慢的脚本编程语言,但Python代码相较于C++更易书写,高精度计算也更易在Python中实现。由于Python存在全局解释器锁,所以该脚本的计算速度很大程度上取决于CPU的单核性能,迭代的次数,以及要求的精确度。

对于现代消费级CPU(主频1.8Ghz)来说,使用该脚本计算十万位精确度的 π 值,大约需要2~3个小时。服务器专用的CPU则速度更快,因为其单核性能优于消费级的产品。

在库的选用方面,Decimal库的计算效率偏低,因为它的计算结果和过程依靠字符串来实现。

但Decimal可以精确计算数十万乃至数百万位的小数,且不会出现因二进制运算产生的循环小数误差,这一点是Numpy等一众基于C++编写的Python算术库所不具备的,在计算π的精确值这个问题上,我们并无其他更优的选择。

一万位以内的π值,脚本可以在3~5分钟之内完成计算,一千位则几秒便可完成计算。这是因为Python运行时对常用的数字进行了高速缓存,待高速缓存随着计算精度的升高而用尽,计算速度显著减慢。

完整代码

import math
from tqdm import tqdm
from decimal import *
import sys
import time
import os

Pi = Decimal(0)
Acc = int(input("How many times to calc:"))
getcontext().prec = round(1.4*Acc)
First = Decimal(1) / Decimal(5)
Last = Decimal(1) / Decimal(239)
Flip = False
Count = Decimal(1)


for i in tqdm(range(Acc),position=0,file=sys.stdout,desc="Progress"):
	if Count != Decimal(1):
		if not Flip:
			First = First - (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(5)**(Decimal(2)*Count - Decimal(1)))))
			Last = Last - (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(239)**(Decimal(2)*Count - Decimal(1)))))
			Flip = not Flip
		elif Flip:
			First = First + (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(5)**(Decimal(2)*Count - Decimal(1)))))
			Last = Last + (Decimal(1) / ((Decimal(2)*Count - Decimal(1))*(Decimal(239)**(Decimal(2)*Count - Decimal(1)))))
			Flip = not Flip
		Pi = (Decimal(16) * First) - (Decimal(4) * Last)
	Count = Count + Decimal(1)
	if Count % 100 == 0 or Count == Acc - 1:
		#os.system('cls')  # 清空Windows控制台
		#tqdm.write(str(Pi))
		pass
print(str(Pi))
Python

写在最后

现在,用你自己的电脑计算圆周率并不是什么明智的选择。

因为早就有计算机科学家用超级计算机将圆周率计算到了几万亿位,你在网上也能轻松查询到一百万位圆周率的精确数值,所以大可不必自己用CPU计算。

那么问题来了,用自己的电脑计算 π 有什么意义?

我说,没准儿,它能检测一下你电脑CPU的单核性能如何。

5 1 投票
文章评分
感谢您一直阅读到本文的最后!
如果您觉得本文写得不错,欢迎您转发支持我们。
# # # # #
首页      IT技术      用Python计算圆周率 π
Avatar photo

SheepChef

文章作者

Webmaster

订阅评论
提醒
guest
0 评论
最新
最旧 最多投票
内联反馈
查看所有评论

SheepChef Blog

用Python计算圆周率 π
使用Python精确计算任意位的圆周率。
扫描二维码继续阅读
2024-07-29