在Python上无法准确计算圆周率

7
我是新成员,我将直接着手处理它,因为我整个星期天都在尝试理解它。
我是Python的新手,先前学习了C ++编程的基本中级水平(这是一个10周的大学模块)。
我正在尝试几种迭代技术来计算圆周率,但两种方法都略有不准确,我不确定为什么。
第一种方法是我在大学里学到的-我相信你们中的一些人以前已经看过它。
x=0.0
y=0.0
incircle = 0.0
outcircle = 0.0
pi = 0.0
i = 0
while (i<100000):
    x = random.uniform(-1,1)
    y = random.uniform(-1,1)
    if (x*x+y*y<=1):
        incircle=incircle+1
    else:
        outcircle=outcircle+1
    i=i+1
pi = (incircle/outcircle)
print pi

这是一个在平面上生成随机(x,y)坐标的生成器,坐标轴范围为-1至+1。如果x ^ 2 + y ^ 2 <= 1,则我们知道该点位于坐标轴形成的正方形内的半径为1的圆内。

根据点的位置,计数器会增加incircle或outcircle的值。

然后,π的值是圆内和圆外的值之比。坐标是随机生成的,因此应该是均匀分布的。

然而,即使在非常高的迭代值下,我的Pi的结果始终在3.65左右。

第二种方法是另一种迭代方法,它计算具有增加边数的多边形的周长,直到多边形几乎成为一个圆形,然后,Pi =周长/直径。(我有点作弊,因为编码中有一个math.cos(Pi)项,所以看起来我正在使用Pi来找到Pi,但这只是因为你不能在Python上轻松地使用度来表示角度)。但即使进行高迭代,最终结果似乎也要在3.20左右,这也是错误的。代码在此处:

S = 0.0
C = 0.0
L = 1.0

n = 2.0
k = 3.0
while (n<2000):
    S = 2.0**k
    L = L/(2.0*math.cos((math.pi)/(4.0*n)))
    C = S*L
    n=n+2.0
    k=k+1.0

pi = C/math.sqrt(2.0)
print pi

我记得上C++课时,老师告诉我们这个问题很常见,并不是因为数学问题,而是代码中的某些东西导致的,但我记不清具体原因了。可能与随机数生成有关,或者使用浮点数的限制,或者……任何东西都有可能。甚至可能只是我的数学不行……

有人能想到问题出在哪里吗?

太长不看:尝试计算圆周率,无论我做多少次迭代,都不能精确地得到它。

(还有一点——在第二段代码中,有一行写着S=2.0 ** k。如果将“n”设置为2000以上,S的值就会变得太大,导致代码崩溃。我该怎么解决?)

谢谢!


这是一个数学问题。蒙特卡罗方法给出的是 pi 的近似值,而不是 pi 本身。这个链接应该更准确:http://rosettacode.org/wiki/Pi#Python。 - Rolbrok
我也注意到Python在计算上有时候会稍微有些偏差。例如,当使用 tan(45) 度进行计算时,它返回的结果是0.99999...而不是1。 - Ashwin Gupta
@AshwinGupta 这不仅是Python的缺陷,而是任何实现浮点运算的语言都会有的问题。此外,tan(45)等于1。 - Reti43
你试过math.pi吗? - Anthony Pham
@Reti43,我的错,我是指tan 45。打错了。 - Ashwin Gupta
顺便说一下,Python 内部使用 float64,其最大指数为 +307。这意味着您无法表示 2.0 ** 1024。 - Reti43
2个回答

7
你的第一个版本的算法应该更像这样:

from __future__ import division, print_function

import sys
if sys.version_info.major < 3:
    range = xrange

import random 


incircle = 0
n = 100000
for n in range(n):
    x = random.random()
    y = random.random()
    if (x*x + y*y <= 1):
        incircle += 1
pi = (incircle / n) * 4
print(pi)

输出:

3.14699146991

这更接近了。增加n 可以使得逼近π的精度更高。

算法仅考虑单位圆的一个象限,即半径为1的部分。

一个四分之一圆形面积的公式为:

area_c = (pi * r **2) / 4

对于包含该圆的正方形:

area_s = r **2

其中r为圆的半径。

现在的比率是:

area_c / area_s

替换上述方程式,重新排列即可得到以下结果:
pi = 4 * (area_c / area_s)

蒙特卡罗方法,只需用一个非常大的数字替换两个区域。通常使用随机投掷飞镖的类比。


真糟糕,我刚想发布这个 : )。哦,不过反正我也不是 Python 的粉丝。 - Mike Wise
这是中间点数与所有点数之比的四倍,而不仅仅是外部点数...你应该提到这一点。 - Mike Wise
啊,好的,谢谢!我现在明白了。我承认我只是凭记忆(一年多以前)回顾了一下代码,并且相当愚蠢地没有停下来思考它背后的基本数学原理,似乎我认为它只是(内圆/外圆),但现在,在翻找一些旧文件并找到原始的C++代码之后,它确实使用了这里解释的方法。再次感谢。 - Stuart Aitken

2

首先,你需要进行如下计算:

pi = incircle/1000000*4  # 3.145376..

这是落在圆内的点数除以总点数的比例(在我的运行中约为0.785671)。
以半径1为例(random.uniform(-1,1)),总面积为4,因此如果将4乘以落在圆内的点数比例,就可以得到正确答案。

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