使用Monte Carlo Simulation限制计算π

时间:2018-09-12 12:38:30

标签: javascript math montecarlo pi

我问了一个与此非常类似的问题,所以我将在最后提及以前的解决方案,我有一个website,它在将客户端的CPU存储到服务器的同时使用客户端的CPU计算π,到目前为止,我已经得到了:

'701.766.448.388'指向圆内,总共为'893.547.800.000',这些数字是使用此代码计算的。 (示例见https://jsfiddle.net/d47zwvh5/2/

let inside = 0;
let size = 500;

for (let i = 0; i < iterations; i++) {
  var Xpos = Math.random() * size;
  var Ypos = Math.random() * size;

  var dist = Math.hypot(Xpos - size / 2, Ypos - size / 2);

  if (dist < size / 2) {
    inside++;
  }
}

问题

  

(4 * 701.766.448.388)/ 893.547.800.000 = 3,141483638

我们得到的结果是正确的,直到第四个数字4应该为5。

以前的问题:

  1. 我搞砸了距离计算。
  2. 我将圆圈的位置从0 ... 499设置为0 ... 500
  3. 我没有使用float,这降低了“分辨率”

免责声明

可能只是我已经达到极限,但本次演示使用了100万积分并获得了3.16。考虑到我有大约9000亿我认为这可能更准确。

我确实知道,如果我想计算π,这不是正确的方法,但是我只是想确保一切都正确,所以我希望有人能发现错误或我只需要更多“点”。

编辑:关于数字的不现实性有很多提及,这些提及在哪里正确,我现在将它们更新为正确的。

2 个答案:

答案 0 :(得分:2)

您可以轻松地估计应该得到哪种错误(错误条),这就是蒙特卡洛的魅力所在。为此,您必须计算第二个动量并估算方差和标准偏差。好消息是,收集的值将与您的均值相同,因为您只是在1比1后加1。

然后,您可以获取模拟sigma的估计值以及所需值的误差线。抱歉,我对Javascript的了解不够,所以这里的代码在C#中:

using System;

namespace Pi
{
    class Program
    {
        static void Main(string[] args)
        {
            ulong N = 1_000_000_000UL; // number of samples
            var rng = new Random(312345); // RNG

            ulong v  = 0UL; // collecting mean values here
            ulong v2 = 0UL; // collecting squares, should be the same as mean
            for (ulong k = 0; k != N; ++k) {
                double x = rng.NextDouble();
                double y = rng.NextDouble();

                var r = (x * x + y * y < 1.0) ? 1UL : 0UL;

                v  += r;
                v2 += r * r;
            }

            var mean = (double)v / (double)N;
            var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL)); // variance
            var stdd = Math.Sqrt(varc); // std.dev, should be sqrt(Pi/4 (1-Pi/4))
            var errr = stdd / Math.Sqrt(N);

            Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");

            mean *= 4.0;
            errr *= 4.0;

            Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
            Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
            Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");
        }
    }
}

在获得10个 9 个样本后

Mean = 0.785405665, StdDev = 0.410540627166729, Err = 1.29824345388086E-05
PI (1 sigma) = 3.14157073026184...3.14167458973816
PI (2 sigma) = 3.14151880052369...3.14172651947631
PI (3 sigma) = 3.14146687078553...3.14177844921447

看起来正确。很容易看出,在理想情况下,方差等于(Pi / 4)*(1-Pi / 4)。确实没有必要计算v2,只需在仿真后将其设置为v

坦率地说,我不知道为什么你没有得到预期的结果。求和的精确度损失可能是答案,或者我怀疑,由于播种和重叠序列(因此实际N大大低于900万亿),您的模拟没有生成独立的样本。

但是使用这种方法,您可以控制错误并检查计算的方式。

更新

我已插入您的电话号码,以表明您明显低估了价值。代码

    N  = 893_547_800_000UL;
    v  = 701_766_448_388UL;
    v2 = v;

    var mean = (double)v / (double)N;
    var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL)); 
    var stdd = Math.Sqrt(varc); // should be sqrt(Pi/4 (1-Pi/4))
    var errr = stdd / Math.Sqrt(N);

    Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");

    mean *= 4.0;
    errr *= 4.0;

    Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
    Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
    Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");

并输出

Mean = 0.785370909522692, StdDev = 0.410564786603016, Err = 4.34332975349809E-07
PI (1 sigma) = 3.14148190075886...3.14148537542267
PI (2 sigma) = 3.14148016342696...3.14148711275457
PI (3 sigma) = 3.14147842609506...3.14148885008647

因此,很明显,您在某处存在问题(代码?表示形式中丢失的准确性?汇总中丢失的准确性?重复/非独立采样?)

答案 1 :(得分:1)

任何FPU操作都会降低您的准确性。为什么不这样做:

let inside = 0;
for (let i = 0; i < iterations; i++)
  {
  var X = Math.random();
  var Y = Math.random();
  if ( X*X + Y*Y <= 1.0 ) inside+=4;
  }

如果我们探测单位圆的第一象限,则不需要将动态范围更改为size,并且可以测试以2乘幂的形式消除sqrt的距离。这些更改将提高精度,并提高速度。

不是JAVASCRIPT编码器,所以我不知道您使用的是哪种数据类型,但是您需要确保不超过其精度。在这种情况下,您需要添加更多的计数器变量以减轻其负担。有关更多信息,请参见:[edit1] integration precision

由于您的数字很大,所以我敢打赌,您已经越过了边界(应该没有小数部分,并且尾随零也是可疑的),例如32位float最多只能存储整数

2^23 = 8388608

并且您的698,565,481,000,000远高于此值,因此即使对该变量执行++操作也会导致精度损失,并且当指数太大时甚至会停止添加...

在整数上这不是问题,但是一旦您根据内部格式越过边界,该值会绕零或取反...但我怀疑这种情况,因为结果将与PI相去甚远。 >