我的算法返回pi的近似值。它的工作原理是
a_2n= math.sqrt(-2*(math.sqrt((n**2)-4*(a_n**2))-n)*n)/2
其中a_n是当n是某个数时的近似值,而a_2n是当n是该数的两倍时的近似值。它从n = 4开始,a_n是2,并应用公式直到n足够高。 n越高,计算越精确,当n> = 2 ^ 22
时,它才会突然停止收敛到pi。这是完整的代码:
import math
def pi(n):
value = 2
round = 4
loop = 2
while round != n:
value= a2n(value,round)
round = round*2
loop +=1
print("n=2^", loop, " pi = ", value)
return value
def a2n(an,n):
return math.sqrt(-2*(math.sqrt((n**2)-4*(an**2))-n)*n)/2
print(pi(2**25))
我很自信数学很好,所以我认为python遇到了更大的数字问题。它从'3.141596553704'变为'3.14167426502175'并从那里变得更糟。
答案 0 :(得分:4)
您正在使用的迭代公式有点恶劣:随着迭代的进行,数量n
变得比an
大得多。所以在表达式中
math.sqrt(n**2-4*an**2)-n
平方根的结果将接近n
,因此外部减法是两个几乎相等的量的减法(在相对意义上)。现在,如果您使用常规的Python浮点数进行计算,这些浮点数具有16位精度的十进制数字,那么减法将在迭代过程中为您提供精确到少数位数的结果。有关此问题的更一般性讨论,请参阅loss of significance上的维基百科页面。
短篇小说:要使用最初编写的公式获取d
pi
个数字,您需要在中间计算中使用多于d
个数字。通过一些工作,您可以证明您需要在内部使用略高于2d
精度的数字,以获得pi的d
个准确数字。即使这样,你也必须要小心,只需要使用尽可能多的迭代:无论你使用多少中间精度,迭代次数太多,精度也会再次失去。
但是这里有一个更好的选择,而不是加倍中间精度,那就是重写你的公式,以便它首先避免重要性的损失。如果您将sqrt(n**2-4*an**2)-n
乘以共轭表达式sqrt(n**2-4*an**2)+n
,则得到-4*an**2
。因此,原始差异可以重写为-4*an**2/(sqrt(n**2-4*an**2)+n)
。将其插入原始公式并简化一点会导致迭代步骤如下所示:
def a2n(an, n):
return an*math.sqrt(2*n/(math.sqrt(n*n-4*an*an)+n))
从数学的角度来说,与<{1}}函数的计算完全相同,但从数值的角度来看,它的表现要好得多。
如果使用此迭代步骤代替原始步骤,您将看到更小的舍入误差,并且您应该能够使用Python浮点数获得高达15位的精度。实际上,使用这个新迭代运行代码,我在30次迭代后得到a2n
的值,这只是通过单个ulp(最后一个单位)的pi的最佳双精度近似值。 / p>
要获得更多数字,我们需要使用decimal
模块。这是一段代码,基于您的代码,但使用我建议的修改计算进行迭代,使用十进制模块获取精确到51位有效数字的pi值。它使用55位有效数字的内部精度,以便累积舍入误差。
3.1415926535897927
这是结果:
from decimal import getcontext
context = getcontext()
context.prec = 55 # Use 55 significant digits for all operations.
sqrt = context.sqrt # Will work for both ints and Decimal objects,
# returning a Decimal result.
def step(an, n):
return an*sqrt(2*n/(sqrt(n*n-4*an*an)+n)), n*2
def compute_pi(iterations):
value, round = 2, 4
for k in range(iterations):
value, round = step(value, round)
return value
pi_approx = compute_pi(100)
print("pi = {:.50f}".format(pi_approx))
使用原始公式,中间计算需要超过100的精度。
答案 1 :(得分:2)
可能不是python本身有这个问题,但在2 ^ 22你遇到python在内部使用的类型的数值问题。
对于python使用的64位浮点数,你得到52位的尾数;这会将您的准确度限制在逗号后面的大约16位(十进制)数位。
编辑:我真的不知道如何回到Matlab有帮助 - 默认情况下,Matlab内部通常也使用相同的64位浮点类型。