Python Pi 近似
Python Pi approximation
所以我必须用以下方法来近似 Pi:4*(1-1/3+1/5-1/7+1/9-...)。它也应该基于迭代次数。所以函数应该是这样的:
>>> piApprox(1)
4.0
>>> piApprox(10)
3.04183961893
>>> piApprox(300)
3.13825932952
但它是这样工作的:
>>> piApprox(1)
4.0
>>> piApprox(10)
2.8571428571428577
>>> piApprox(300)
2.673322240709928
我做错了什么?这是代码:
def piApprox(num):
pi=4.0
k=1.0
est=1.0
while 1<num:
k+=2
est=est-(1/k)+1/(k+2)
num=num-1
return pi*est
这就是您正在计算的内容:
4*(1-1/3+1/5-1/5+1/7-1/7+1/9...)
您可以通过在循环末尾添加 k += 2
来修复它:
def piApprox(num):
pi=4.0
k=1.0
est=1.0
while 1<num:
k+=2
est=est-(1/k)+1/(k+2)
num=num-1
k+=2
return pi*est
您计算迭代次数的方式也是错误的,因为您当时添加了两个元素。
这是一个更简洁的版本,returns 您期望 10 次和 300 次迭代的输出:
def approximate_pi(rank):
value = 0
for k in xrange(1, 2*rank+1, 2):
sign = -(k % 4 - 2)
value += float(sign) / k
return 4 * value
这是相同的代码,但更紧凑:
def approximate_pi(rank):
return 4 * sum(-float(k%4 - 2) / k for k in xrange(1, 2*rank+1, 2))
def piApprox(num):
pi=4.0
k=3.0
est=1.0
while 1<num:
est=est-(1/k)+1/(k+2)
num=num-1
k+=4
return pi*est
也可用于实际任务math.pi
重要修改:
谁期望这个近似值产生圆周率——引自 Wikipedia:
It converges quite slowly, though – after 500,000 terms, it produces
only five correct decimal digits of π
原回答:
这是一个有教育意义的例子。您尝试使用快捷方式并尝试通过在同一迭代中处理 k
的两个步骤来实现被加数的 "oscillating" 符号。但是,您每次迭代仅调整 k
一步。
通常,至少在数学中,(-1)**i
可以实现振荡符号。所以,我选择了这个来实现更具可读性:
def pi_approx(num_iterations):
k = 3.0
s = 1.0
for i in range(num_iterations):
s = s-((1/k) * (-1)**i)
k += 2
return 4 * s
如您所见,我稍微更改了您的方法,以提高可读性。您不需要在 while 循环中检查 num
,也没有特别需要您的 pi
变量。你的est
实际上是一个逐步增长的总和,为什么不叫它s
("sum"是Python中的内置关键字)。根据您的公式,最后将总和乘以 4
。
测试:
>>> pi_approx(100)
3.1514934010709914
然而,收敛性不是特别好:
>>> pi_approx(100) - math.pi
0.009900747481198291
你的预期输出有点不稳定,因为你的 piApprox(300)
(根据你的说法应该是 3.13825932952
)离 PI 太远了。你是怎么想出来的?这可能会受到累积数值误差的影响吗?
编辑
关于函数在 10 次和 300 次迭代后应该 return 的内容,我不会太相信这本书。确实,经过 10 个步骤后的中间结果应该完全没有数字错误。在那里,你是否同时走两步k
实际上是有区别的。所以这很可能是我的 pi_approx(10)
和书本之间的区别。对于 300 次迭代,数值错误可能会严重影响书中的结果。如果这是一本旧书,并且他们已经在 C 中实现了他们的示例,可能使用单精度,那么结果的很大一部分可能是由于数值误差的累积(注意:这是一个很好的例子,说明你有多糟糕受数值错误的影响:小值和大值的重复 sum,它不会变得更糟!)。
重要的是您已经研究了数学(圆周率的公式),并且您已经实现了近似该公式的工作 Python 版本。这就是本书的学习目标,所以继续解决下一个问题:-)。
这里有一个稍微简单的版本:
def pi_approx(num_terms):
sign = 1. # +1. or -1.
pi_by_4 = 1. # first term
for div in range(3, 2 * num_terms, 2): # 3, 5, 7, ...
sign = -sign # flip sign
pi_by_4 += sign / div # add next term
return 4. * pi_by_4
这给出了
>>> for n in [1, 10, 300, 1000, 3000]:
... print(pi_approx(n))
4.0
3.0418396189294032
3.1382593295155914
3.140592653839794
3.1412593202657186
虽然所有这些答案都是非常好的近似值,但如果您使用的是 Madhava-Leibniz 系列,那么根据本网站,您应该在前 21 个术语中得出"an approximation of π correct to 11 decimal places as 3.14159265359":https://en.wikipedia.org/wiki/Approximations_of_%CF%80
因此,更准确的解决方案可能是以下任何变体:
import math
def estimate_pi(terms):
ans = 0.0
for k in range(terms):
ans += (-1.0/3.0)**k/(2.0*k+1.0)
return math.sqrt(12)*ans
print(estimate_pi(21))
输出:3.141592653595635
所以我必须用以下方法来近似 Pi:4*(1-1/3+1/5-1/7+1/9-...)。它也应该基于迭代次数。所以函数应该是这样的:
>>> piApprox(1)
4.0
>>> piApprox(10)
3.04183961893
>>> piApprox(300)
3.13825932952
但它是这样工作的:
>>> piApprox(1)
4.0
>>> piApprox(10)
2.8571428571428577
>>> piApprox(300)
2.673322240709928
我做错了什么?这是代码:
def piApprox(num):
pi=4.0
k=1.0
est=1.0
while 1<num:
k+=2
est=est-(1/k)+1/(k+2)
num=num-1
return pi*est
这就是您正在计算的内容:
4*(1-1/3+1/5-1/5+1/7-1/7+1/9...)
您可以通过在循环末尾添加 k += 2
来修复它:
def piApprox(num):
pi=4.0
k=1.0
est=1.0
while 1<num:
k+=2
est=est-(1/k)+1/(k+2)
num=num-1
k+=2
return pi*est
您计算迭代次数的方式也是错误的,因为您当时添加了两个元素。
这是一个更简洁的版本,returns 您期望 10 次和 300 次迭代的输出:
def approximate_pi(rank):
value = 0
for k in xrange(1, 2*rank+1, 2):
sign = -(k % 4 - 2)
value += float(sign) / k
return 4 * value
这是相同的代码,但更紧凑:
def approximate_pi(rank):
return 4 * sum(-float(k%4 - 2) / k for k in xrange(1, 2*rank+1, 2))
def piApprox(num):
pi=4.0
k=3.0
est=1.0
while 1<num:
est=est-(1/k)+1/(k+2)
num=num-1
k+=4
return pi*est
也可用于实际任务math.pi
重要修改: 谁期望这个近似值产生圆周率——引自 Wikipedia:
It converges quite slowly, though – after 500,000 terms, it produces only five correct decimal digits of π
原回答:
这是一个有教育意义的例子。您尝试使用快捷方式并尝试通过在同一迭代中处理 k
的两个步骤来实现被加数的 "oscillating" 符号。但是,您每次迭代仅调整 k
一步。
通常,至少在数学中,(-1)**i
可以实现振荡符号。所以,我选择了这个来实现更具可读性:
def pi_approx(num_iterations):
k = 3.0
s = 1.0
for i in range(num_iterations):
s = s-((1/k) * (-1)**i)
k += 2
return 4 * s
如您所见,我稍微更改了您的方法,以提高可读性。您不需要在 while 循环中检查 num
,也没有特别需要您的 pi
变量。你的est
实际上是一个逐步增长的总和,为什么不叫它s
("sum"是Python中的内置关键字)。根据您的公式,最后将总和乘以 4
。
测试:
>>> pi_approx(100)
3.1514934010709914
然而,收敛性不是特别好:
>>> pi_approx(100) - math.pi
0.009900747481198291
你的预期输出有点不稳定,因为你的 piApprox(300)
(根据你的说法应该是 3.13825932952
)离 PI 太远了。你是怎么想出来的?这可能会受到累积数值误差的影响吗?
编辑
关于函数在 10 次和 300 次迭代后应该 return 的内容,我不会太相信这本书。确实,经过 10 个步骤后的中间结果应该完全没有数字错误。在那里,你是否同时走两步k
实际上是有区别的。所以这很可能是我的 pi_approx(10)
和书本之间的区别。对于 300 次迭代,数值错误可能会严重影响书中的结果。如果这是一本旧书,并且他们已经在 C 中实现了他们的示例,可能使用单精度,那么结果的很大一部分可能是由于数值误差的累积(注意:这是一个很好的例子,说明你有多糟糕受数值错误的影响:小值和大值的重复 sum,它不会变得更糟!)。
重要的是您已经研究了数学(圆周率的公式),并且您已经实现了近似该公式的工作 Python 版本。这就是本书的学习目标,所以继续解决下一个问题:-)。
这里有一个稍微简单的版本:
def pi_approx(num_terms):
sign = 1. # +1. or -1.
pi_by_4 = 1. # first term
for div in range(3, 2 * num_terms, 2): # 3, 5, 7, ...
sign = -sign # flip sign
pi_by_4 += sign / div # add next term
return 4. * pi_by_4
这给出了
>>> for n in [1, 10, 300, 1000, 3000]:
... print(pi_approx(n))
4.0
3.0418396189294032
3.1382593295155914
3.140592653839794
3.1412593202657186
虽然所有这些答案都是非常好的近似值,但如果您使用的是 Madhava-Leibniz 系列,那么根据本网站,您应该在前 21 个术语中得出"an approximation of π correct to 11 decimal places as 3.14159265359":https://en.wikipedia.org/wiki/Approximations_of_%CF%80
因此,更准确的解决方案可能是以下任何变体:
import math
def estimate_pi(terms):
ans = 0.0
for k in range(terms):
ans += (-1.0/3.0)**k/(2.0*k+1.0)
return math.sqrt(12)*ans
print(estimate_pi(21))
输出:3.141592653595635