浮点精度问题在计算π时发生。

huangapple go评论64阅读模式
英文:

Floating Point Accuracy Problems While Calculating Pi

问题

我正在尝试使用随机数来估算π的值。我开始使用的示例显然存在一些问题,这其实是一件好事,因为这意味着我必须理解它,以便修复它。

# 期望结果 3.141592653589793238
#
from math import sqrt
from random import random
# 随机点的数量
N = 100000
# 内部的点的数量
I = 0
for i in range(N):
  # 生成一个在1x1正方形内的随机点
  x = random()
  y = random()
  # 这个点在圆内吗?
  # r = sqrt(x**2 + y**2)
  # if r < 1:
  if (x*x + y*y) < 1:    # 更新 2
    I += 1
# print("Pi=" + str(4*I/N))
print("Pi=" + str(4*I)/N)) # 更新 3

期望结果:

3.141592653589793238

实际结果:

  • 100,000 次迭代
Pi=3.14736
Pi=3.14448
Pi=3.14424
  • 1,000,000 次迭代
Pi=3.141496
Pi=3.141356
Pi=3.138

我本来期望结果更一致。如何增加计算的精度?

更新:
我修改了代码以去掉平方根和指数运算,如评论中建议的。我将迭代次数增加到 4,000,000 但效果并不明显。它仍然只精确到大约 2 位小数。如何增加计算的精度?(标题中曾提到此问题,但被编辑掉了)

  • 2,000,000 次迭代
Pi=3.141342
Pi=3.141328
Pi=3.143074
Pi=3.139084
  • 4,000,000 次迭代
Pi=3.142605
Pi=3.141509
Pi=3.140663
Pi=3.143194

更新 2:
我将最终计算更改如下:

import decimal

result = decimal.Decimal(4*I/N)
print("Pi=" + str(result))
  • 4,000,000 次迭代使用 decimal.Decimal()
Pi=3.14080899999999996197175278211943805217742919921875
Pi=3.143496999999999985675458447076380252838134765625
Pi=3.14080899999999996197175278211943805217742919921875
Pi=3.141859999999999875086587053374387323856353759765625

这个最新的测试看起来我已经获得了 3 位小数的精度,但结果看起来仍然有很多 99999 或 00000。还有其他可能发生什么的想法吗?

最后,进行了一些更大的迭代,但我认为精度提高的幅度有限。

  • 100,000,000 次迭代(是的,1 亿次)
Pi=3.141542439999999825062104719108901917934417724609375
Pi=3.141720879999999826992507223621942102909088134765625
Pi=3.141750519999999990972128216526471078395843505859375
Pi=3.14174343999999994281324688927270472049713134765625
Pi=3.14132400000000000517275111633352935314178466796875
  • 1,000,000,000 次迭代(1 十亿次,老电脑上需要一段时间)
Pi=3.141603959999999862162667341181077063083648681640625

更新 3:
我按照 @Michael Butscher 的建议更改了最终计算中的括号位置,但即使进行了 10 亿次迭代,我仍然只能得到一致的 3 位小数精度。也许我碰到了其他一些限制,也许是Python随机数的伪随机性?我将这个练习视为完成,并将我的发现纳入我的实际项目中。

好吧,我抵挡不住诱惑,也进行了 100 亿次迭代。令人惊讶的是,结果并没有有太大差异。

  • 1,000,000 次迭代
Pi=3.142056
Pi=3.136428
Pi=3.1407
Pi=3.141612
  • 10,000,000 次迭代
Pi=3.142806
Pi=3.142266
Pi=3.141996
Pi=3.1422232
  • 100,000,000 次迭代
Pi=3.14151576
Pi=3.1417604
Pi=3.1415038
Pi=3.1413738
  • 1,000,000,000 次迭代
Pi=3.141553108
Pi=3.1415895
Pi=3.141629112
  • 10,000,000,000 次迭代(100亿次)
    执行需要约 12 小时
Pi=3.1416011832
英文:

I'm trying to use random numbers to estimate the value of Pi.
The example I started with obviously has some problems which is a good thing as it means I have to understand it so I can fix it.

# Expected Result 3.141592653589793238
#
from math import sqrt
from random import random
# number of fandom points
N=100000
# number of points inside
I=0
for i in range(N):
  #print(&quot;I=&quot;+str(I))
  #Generate random point in 1x1 square
  x=random()
  y=random()
  # Is the point inside the circle?
  # r=sqrt(x**2 + y**2)
  #print(str(r))
  #if r&lt;1:
  If (x*x + y*y) &lt;1:    # Update 2
    I+=1
# print(&quot;Pi=&quot; + str(4*I/N))
print(&quot;Pi=&quot; + str(4*I)/N)) # Update 3

Expected Result

3.141592653589793238

Actual Results

  • 100,000 Iterations

      Pi=3.14736
      Pi=3.14448
      Pi=3.14424
    
  • 1,000,000 Iterations

      Pi=3.141496
      Pi=3.141356
      Pi=3.138
    

I would have expected the results to be more consistent.
How can I add more precision to the calculations?

UPDATE <br>
I modified the code to remove sqrt and ** as recommended in the comments. I cranked up the iterations to 4,000,000 but it didn’t make a lot of difference. It is still only accurate to about 2 decimal places. How can I add more precision to the calculation. (That question was in the title but it was edited out)

2,000,000 iterations

Pi=3.141342
Pi=3.141328
Pi=3.143074
Pi=3.139084

4,000,000 iterations

Pi=3.142605
Pi=3.141509
Pi=3.140663
Pi=3.143194

UPDATE 2 <br>
I changed the final calculation to the following.

import decimal

result=decimal.Decimal(4*I/N)
print(&quot;Pi=&quot; + str(result))

4,000,000 iterations with decimal.Decimal()

Pi=3.14080899999999996197175278211943805217742919921875
Pi=3.143496999999999985675458447076380252838134765625
Pi=3.14080899999999996197175278211943805217742919921875
Pi=3.141859999999999875086587053374387323856353759765625

40,000,000 iterations with

decimal.Decimal()
Pi=3.141386900000000093058361017028801143169403076171875
Pi=3.1414208999999999605279299430549144744873046875
Pi=3.1414591999999998961357050575315952301025390625
Pi=3.14168000000000002813749233609996736049652099609375

This latest test looks like I have achieved accuracy of 3 decimal places but the results still look odd with big rows of 99999 or 00000. Any other ideas what is going on here?

Last a couple of larger itterations but I think the increase in accuracy is diminishing

100,000,000 Iterations (yes 100 million)

Pi=3.141542439999999825062104719108901917934417724609375
Pi=3.141720879999999826992507223621942102909088134765625
Pi=3.141750519999999990972128216526471078395843505859375
Pi=3.14174343999999994281324688927270472049713134765625
Pi=3.14132400000000000517275111633352935314178466796875

1,000,000,000 Iterations (1 billion too a while on my old laptop :))

Pi=3.141603959999999862162667341181077063083648681640625

Update 3 <BR>
I moved the brackets in final calculation as described by @Michael Butscher however even with 1 billion iterations the I only consistently obtained 3 decimal places accurately. Maybe I have hit some other limitation perhaps the sudo randomness of Python random numbers? I'll call this exercise done and incorporate my findings into my actual project.
OK I couldn't resist I did 10 billion as well
Surprisingly it didn't make much difference

1,000,000 Iterations

Pi=3.142056
Pi=3.136428
Pi=3.1407
Pi=3.141612

10,000,000 Iterations

Pi=3.142806
Pi=3.142266
Pi=3.141996
Pi=3.1422232

100,000,000 Iterations

Pi=3.14151576
Pi=3.1417604
Pi=3.1415038
Pi=3.1413738

1,000,000,000

Pi=3.141553108
Pi=3.1415895
Pi=3.141629112

10,000,000,000 Iterations (10 Billion)
Took about 12 hours to execute

Pi=3.1416011832

答案1

得分: 0

最终的除法目前如下进行:

result = decimal.Decimal(4*I/N)

但这意味着除法使用浮点数算术进行,会产生不精确的结果,如3.141603959999999862162667341181077063083648681640625,而不是3.14160396

此外,对于非常大的整数,除法可能由于溢出而产生无意义的结果。

与此相反,可以首先将整数精确地转换为一个Decimal,然后再除以另一个整数:

result = decimal.Decimal(4*I) / N

这次除法使用Decimal的实现,对于定义的精度(默认为28位数字)是精确的。

英文:

The final division is currently done as

result=decimal.Decimal(4*I/N)

but this means that the division is using float arithmetic which produces imprecise results like 3.141603959999999862162667341181077063083648681640625 instead of 3.14160396.

Moreover the division can produce nonsense due to an overflow for very large integers to divide.

Instead of that, the int can be converted exactly to a Decimal first and then be divided by another int:

result=decimal.Decimal(4*I)/N

This time the division uses the implementation for Decimal which is exact for the defined precision (28 digits by default).

答案2

得分: 0

以下是翻译好的代码部分:

这是我的最终解决方案所有内容都在一个地方
我希望有一个更好的结果也许这需要更多的迭代

from math import sqrt
from random import random
from decimal import Decimal

# 随机点的数量
N = 10000000
# 内部点的数量
I = 0
for i in range(N):
  # 在1x1正方形内生成随机点
  x = Decimal(str(random()))
  y = Decimal(str(random()))
  # 点是否在圆内?
  if (x*x + y*y) < Decimal("1"):    
    I += 1
result = Decimal(4 * I) / N
print("预期结果= 3.141592653589793238")
print("          结果=" + str(result))

# 预期结果 3.141592653589793238
# 结果=3.141676  迭代次数=10,000,000
# 结果=3.1418548 迭代次数=10,000,000
英文:

Here is my final solution all in one place.
I was hoping for a better result. Perhaps this needs more iterations.

from math import sqrt
from random import random
from decimal import Decimal

# number of random points
N=10000000
# number of points inside
I=0
for i in range(N):
  #Generate random point in 1x1 square
  x=Decimal(str(random()))
  y=Decimal(str(random()))
  # Is the point inside the circle?
  if (x*x + y*y) &lt; Decimal(&quot;1&quot;):    
    I+=1
result=Decimal(4*I)/N
print( &quot;Expected Result= 3.141592653589793238 &quot;)
print(&quot;          Result=&quot;+str(result))

# Exp    3.141592653589793238
# Result=3.141676  Iterations=10,000,000
# Result=3.1418548 Iterations=10,000,000

huangapple
  • 本文由 发表于 2023年3月7日 19:05:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75661181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定