Search code examples
javarandommontecarlopi

Monte Carlo calculation of pi using randomly generated data in Java


I'm working on a program that calculates pi based on randomly generated float numbers that represent x,y co-ordinates on a graph. Each x, y co-ordinate is raised by the power of 2 and stored in two separate arrays. The co-ordinates are distributed uniformly on a graph of interval of 0,1.

The program adds the x, y co-ordinates and if they are less than 1 then the points are located within a circle of diameter 1, illustrated in the diagram below.

enter image description here

I then used the formula,

π ≈ 4 w / n

to work out pi. Where, w is the count of the points within the circle and n is the number of x or y co-ordinates within the arrays.

When I set n up to 10,000,000 (the size of the array) it generates the most accurate calculation of pi of 15-16 decimal places. However after dedicating 4GB of RAM to the run config and setting n to 100,000,000 pi ends up being 0.6710...

I was wondering why this may be happening? Sorry if this is a stupid question.. code is below.

import java.text.DecimalFormat;
import java.util.Random;

public class random_pi {

    public random_pi() {

        float x2_store[] = new float[10000000];
        float y2_store[] = new float[10000000];
        float w = 0;

        Random rand = new Random();
        DecimalFormat df2 = new DecimalFormat("#,###,###");

        for (int i = 0; i < x2_store.length; i++) {
            float x2 = (float) Math.pow(rand.nextFloat(), 2);
            x2_store[i] = x2;
            float y2 = (float) Math.pow(rand.nextFloat(), 2);
            y2_store[i] = y2;
        }

        for (int i = 0; i < x2_store.length; i++) {
            if (x2_store[i] + y2_store[i] < 1) {
                w++;
            }
        }

        System.out.println("w: "+w);
        float numerator = (4*w);
        System.out.printf("4*w: " + (numerator));
        System.out.println("\nn: " + df2.format(x2_store.length));
        float pi = numerator / x2_store.length;

        String fmt = String.format("%.20f", pi);
        System.out.println(fmt);

        String pi_string = Double.toString(Math.abs(pi));
        int intP = pi_string.indexOf('.');
        int decP = pi_string.length() - intP - 1;
        System.out.println("decimal places: " + decP);
    }

    public static void main(String[] args) {
        new random_pi();
    }
}

Solution

  • The problem is here:

    float w = 0;
    float numerator = (4*w);
    

    float precision is not enough, change it to int or double:

    Like this working sample code:

    import java.text.DecimalFormat;
    import java.util.Random;
    
    public class random_pi {
    
        public random_pi() {
    
            float x2_store[] = new float[100000000];
            float y2_store[] = new float[100000000];
            int w = 0;
    
            Random rand = new Random();
            DecimalFormat df2 = new DecimalFormat("#,###,###");
    
            for (int i = 0; i < x2_store.length; i++) {
                float x2 = (float) Math.pow(rand.nextFloat(), 2);
                x2_store[i] = x2;
                float y2 = (float) Math.pow(rand.nextFloat(), 2);
                y2_store[i] = y2;
            }
    
            for (int i = 0; i < x2_store.length; i++) {
                if (x2_store[i] + y2_store[i] < 1) {
                    w++;
                }
            }
    
            System.out.println("w: "+w);
            int numerator = (4*w);
            System.out.printf("4*w: " + (numerator));
            System.out.println("\nn: " + df2.format(x2_store.length));
            float pi = ((float)numerator) / x2_store.length;
    
            String fmt = String.format("%.20f", pi);
            System.out.println(fmt);
    
            String pi_string = Double.toString(Math.abs(pi));
            int intP = pi_string.indexOf('.');
            int decP = pi_string.length() - intP - 1;
            System.out.println("decimal places: " + decP);
        }
    
        public static void main(String[] args) {
            new random_pi();
        }
    }
    

    output:

    w: 78544041
    4*w: 314176164
    n: 100,000,000
    3.14176154136657700000
    decimal places: 15
    

    And you don't need to store the results, like this working sample code:

    import java.text.DecimalFormat;
    import java.util.Random;
    
    public class pi {
    
        public pi() {
            double n=100000000; 
            double w = 0;
    
            Random rand = new Random();
            DecimalFormat df2 = new DecimalFormat("#,###,###");
    
            for (int i = 0; i < n; i++) {
                double x = rand.nextFloat(); 
                double y = rand.nextFloat();  
                if ((x*x + y*y) < 1.0) w++;
            }
    
            System.out.println("w: "+w);//w: 7852372.0
            double numerator = (4*w);
            System.out.printf("4*w: " + (numerator));//4*w: 3.1409488E7
            System.out.println("\nn: " + df2.format(n));//n: 10,000,000
            double pi = numerator / n;
    
            final String fmt = String.format("%.20f", pi);
            System.out.println(fmt);//3.14094877243042000000
    
            String pi_string = Double.toString(Math.abs(pi));
            int intP = pi_string.indexOf('.');
            int decP = pi_string.length() - intP - 1;
            System.out.println("decimal places: " + decP);//decimal places: 14
        }
    
        public static void main(String[] args) {
            new random_pi();
        }
    }
    

    output:

    w: 78539606
    4*w: 314158424
    n: 100,000,000
    3.14158439636230470000
    decimal places: 16