Python Pi近似值计算

3

所以我必须用以下方法近似计算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

我做错了什么?这是代码:
```html

这里是代码:

```
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

@ScottHunter piApprox(2) 的输出结果为 3.466666666666667 - user4511836
你确定 piApprox(300) 应该返回 3.13825932952 吗?因为那不是 PI。然而,如果正确实现了公式,它是可行的。 - Dr. Jan-Philip Gehrcke
@Jan-PhilipGehrcke 我正在解决一本书上的任务,也许它只是被错误定义了,我永远不会按照他们的要求完成这个任务。 - user4511836
6个回答

2

这是你要计算的内容:

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

同时,您计算迭代次数的方式是错误的,因为您每次添加两个元素。

这是一个更清晰的版本,可以返回您期望的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))

仍然没有给我需要的问题答案。它应该执行 piApprox(10) 并输出 3.04183961893,但是它输出了 3.1941879092319425 - user4511836
经过编辑后的版本确实在300次迭代中返回了3.1382593295155914。因此,导致偏差的不是数值噪声。结论是该公式不是一种好的数值逼近PI的方法。--编辑:确实,我已将此添加到我的答案中。 - Dr. Jan-Philip Gehrcke

1
重要修改: 谁认为这个近似值可以得到圆周率——引用自维基百科
它收敛相当慢,经过500,000项之后,只能产生5位正确的小数点π。
原回答: 这是一个教育性的例子。您尝试使用快捷方式,并尝试通过在同一迭代中处理k的两个步骤来实现“振荡”符号。然而,您每次迭代只调整了一个步骤的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)离π值太远了。你是怎么想到这个数值的?这可能受到累积数值误差的影响吗? 编辑 我不太信任书中关于函数在10次和300次迭代后应返回什么的描述。实际上,在10步之后,中间结果应该相当自由地处理数值误差。事实上,这里是否同时使用两个k步骤会有所不同。因此,这很可能是我pi_approx(10)和书中结果之间的区别。对于300次迭代,数值误差可能会严重影响书中的结果。如果这是一本旧书,并且他们在C语言中实现了他们的示例,可能使用单精度,那么结果的相当一部分可能是由于数值误差的累积造成的(注意:这是一个数值误差如何影响你的极端案例:小数值和大数值的重复总和,它不会更糟!)。
重要的是,您已经查看了数学内容(PI公式),并实现了一个工作的Python版本来近似该公式。这是本书的学习目标,所以继续解决下一个问题吧:-)。

仍然给出错误的输出,请查看第一个答案。 - user4511836

0
def pi_approx(pts: List[List[float]]) -> float:
points_that_satisfy_the_formula = []
for i in pts:
    point = i
    x = point[0]
    y = point[1]
    if math.pow(x, 2) + math.pow(y, 2) <= 1:
        points_that_satisfy_the_formula.append(point)
return 4 * (len(points_that_satisfy_the_formula)/ len(pts))

0
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


0

这里有一个稍微简单一些的版本:

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

0

虽然所有这些答案都是非常好的近似值,但如果您使用Madhava-Leibniz级数,则应该在前21项中得出“正确到11位小数的π的近似值为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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接