
// PiMonte.java
// Andrew Davison, ad@coe.psu.ac.th, April 2023

/* Estimation of Pi using Monte Carlo Simulation.

   The idea is to generate random (x,y) points in a 
   square with sides 2 units centered on (0,0). Imagine a 
   circle inscribed in the square. We calculate the ratio 
   of number of points that lie inside the circle and 
   the total number of generated points. 

   This ratio == pi/4, since the side == 2, so the circle
   radius == 1.

   https://en.wikipedia.org/wiki/Monte_Carlo_method

   This ratio will take a while to approach pi/4 depending
   on the sample size n, with the standard error varying
   proportional to 1/sqrt(n).

   https://en.wikipedia.org/wiki/Variance_reduction
 and 
   https://blogs.sas.com/content/iml/2016/03/14/monte-carlo-estimates-of-pi.html

   This code runs with increasing sample sizes, and prints out
   the resulting pi estimate and its error compared to Math.PI.
   The error varies as expected.

   Usage:
     > compile PiMonte.java
     > run PiMonte
*/

import java.util.*;
import java.math.*;
import java.text.*;


public class PiMonte
{

  public static void main(String[] args)
  {
    Random rand = new Random();
    System.out.println("Math.PI:    " + Math.PI);

    long[] numPts = { 1000, 10000, 100000, 1000000, 
                      10000000,  100000000 };
    System.out.println("PI Estimation:");
    for (long n : numPts) {
	  long circlePts = 0;
      long totalPts = 0;
      for (int i = 0; i < n; i++) {
        double x = rand.nextDouble()*2-1;  // in the range [-1,1]
        double y = rand.nextDouble()*2-1;
        if ((x*x + y*y) <= 1)  // inside circle
          circlePts++;
        totalPts++;
      }
	  
      double piEstimate = ((double)circlePts*4)/totalPts;
      double err = Math.abs((piEstimate - Math.PI)*100/Math.PI);
      System.out.printf("%10d: %.8f; err: %.6f%%\n", n, piEstimate, err);
	}
  } /* main */

}  // end of PiMonte class

