2

I have asked a question very similar to this so I will mention the previous solutions at the end, I have a website that calculates π with the client's CPU while storing it on a server, so far I've got:

'701.766.448.388' points inside the circle, and '893.547.800.000' in total, these numbers are calculated using this code. (working example at: 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++;
  }
}

The problem

(4 * 701.766.448.388) / 893.547.800.000 = 3,141483638

This is the result we get, which is correct until the fourth digit, 4 should be 5.

Previous problems:

  1. I messed up the distance calculation.
  2. I placed the circle's from 0...499 which should be 0...500
  3. I didn't use float, which decreased the 'resolution'

Disclamer

It might just be that I've reached a limit but this demonstration used 1 million points and got 3.16. considering I've got about 900 billion I think it could be more precisely.

I do understand that if I want to calculate π this isn't the right way to go about it, but I just want to make sure that everything is right so I was hoping anyone could spot something wrong or do I just need more 'dots'.

EDIT: There are quite a few mentions about how unrealistic the numbers where, these mentions where correct and I have now updated them to be correct.

Community
  • 1
  • 1
Schotsl
  • 187
  • 1
  • 8
  • 29
  • 2
    1) Those numbers are impossibly large (especially for Javascript)... 2) They are also suspiciously rounded to the nearest million... 3) `Math.random()` might not be the best random generator around. – meowgoesthedog Sep 12 '18 at 13:18
  • @meowgoesthedog You were right, they are displayed wrong (this is also the reason they we're rounded). I've updated it with the precise numbers from the database. I will try to use a more secure random generator altough this will impact speed :( – Schotsl Sep 12 '18 at 14:02
  • 1
    The error is -0.00011, but what is the standard deviation of the estimator ? –  Sep 12 '18 at 15:11
  • What is `size`? In any event, error of the estimate is roughly proportional to the reciprocal of the square root of the sample size. When you go from 1 million to 1 trillion, that is a million-fold increase in sample size but only a thousand-fold increase in the square root of the sample size. Which -- should buy you just 2 or 3 more decimal places of accuracy. This doesn't mean that there is no bug in the code, but that what you are seeing might not be as surprising as you think. – John Coleman Sep 13 '18 at 01:29
  • @JohnColeman size is a variable that defines the circle and square size and thus also the random numbers limit. Thanks for the explanation though, it might just be that I've reached the practical limit with the amount of processing power at my disposal. – Schotsl Sep 14 '18 at 08:19
  • @YvesDaoust I personsanly dont know how I could calculate standard deviation but the answer from Severin Pappadeux seems to give some inside into this. – Schotsl Sep 14 '18 at 08:26

2 Answers2

2

You could easily estimate what kind of error (error bars) you should get, that's the beauty of the Monte Carlo. For this, you have to compute second momentum and estimate variance and std.deviation. Good thing is that collected value would be the same as what you collect for mean, because you just added up 1 after 1 after 1.

Then you could get estimation of the simulation sigma, and error bars for desired value. Sorry, I don't know enough Javascript, so code here is in 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}");
        }
    }
}

After 109 samples I've got

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

which looks about right. It is easy to see that in ideal case variance would be equal to (Pi/4)*(1-Pi/4). It is really not necessary to compute v2, just set it to v after simulation.

I, frankly, don't know why you're getting not what's expected. Precision loss in summation might be the answer, or what I suspect, you simulation is not producing independent samples due to seeding and overlapping sequences (so actual N is a lot lower than 900 trillion).

But using this method you control error and check how computation is going.

UPDATE

I've plugged in your numbers to show that you're clearly underestimating the value. Code

    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}");

And output

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

So, clearly you have problem somewhere (code? accuracy lost in representation? accuracy lost in summation? repeated/non-independent sampling?)

Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
  • I dont know very much C# so its rough on the both of us! Anyway the 900 trillion number is gave was wrong (see edit) its actually 900 billion, which might explain the difference in error? – Schotsl Sep 14 '18 at 08:55
  • @Schotsl ` so its rough on the both of us!` Yep! Wrt 900 billion, I've plugged your numbers into error estimation code, please check update. No, you have a real problem somewhere. – Severin Pappadeux Sep 14 '18 at 15:42
  • Looking over it I really couldn't find any problems, I'll try using a more secure random generator and see where that brings me. – Schotsl Sep 16 '18 at 15:38
  • @Schotsl please keep us informed - I'm curious what the problem would be because it is not obviously wrong, but only slightly so – Severin Pappadeux Sep 17 '18 at 02:01
  • It's been a while but I've finally updated the code to use crypto random so now we wait! – Schotsl Oct 08 '18 at 14:07
  • @Schotsl Great, awaiting results – Severin Pappadeux Oct 09 '18 at 14:24
1

any FPU operation will decrease your accuracy. Why not do something like this:

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;
  }

if we probe first quadrant of unit circle we do not need to change the dynamic range by size and also we can test the distances in powered by 2 form which get rid of the sqrt. These changes should increase the precision and also the speed.

Not a JAVASCRIPT coder so I do not know what datatypes you use but you need to be sure you do not cross its precision. In such case you need to add more counter variables to ease up the load on it. For more info see: [edit1] integration precision.

As your numbers are rather big I bet you crossed the boundary already (there should be no fraction part and trailing zeros are also suspicious) For example 32bit float can store only integers up to

2^23 = 8388608

and your 698,565,481,000,000 is way above that so even a ++ operation on such variable will cause precision loss and when the exponent is too big it even stop adding...

On integers is this not a problem but once you cross the boundary depending on internal format the value wraps around zero or negates ... But I doubd that is the case as then the result would be way off from PI.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • When changing the Javascript with your new snippet, I do understand why the distance calculation is an improvement in speed, but the only advantage of adding 4 over 1 is when you calculate π right? Since at that point you can just divide the inside and outside numbers. Your code does increase speed a lot, just by looking at it appears to be a three fold increase! and I gave the wrong numbers previously (see edit) but the current numbers should work fine in Javascript. – Schotsl Sep 14 '18 at 08:40
  • @Schotsl Yes you can divide by 4 in the final division instead of `+=4` that might also save you 2 bits in the counter if you are near the variable bit-width boundary. As single division does not matter from performance point of view. I was just lazy to write another line of code ... But the major accuracy increase is in avoiding any unnecessary computation on floats – Spektre Sep 14 '18 at 09:05