4
\$\begingroup\$

I have this short program that attempts to compute an approximate value of \$\pi\$:

package net.coderodde.fun;

import java.awt.geom.Point2D;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * This class computes an approximate value of Pi. 
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Feb 23, 2018)
 */
public class MonteCarloPiComputer {

    /**
     * The default radius of the simulated circle.
     */
    private static final double DEFAULT_RADIUS = 0.5;

    /**
     * The random number generator.
     */
    private final Random random;

    public MonteCarloPiComputer(Random random) {
        this.random = 
                Objects.requireNonNull(
                        random, 
                        "The input random number generator is null.");
    }

    public MonteCarloPiComputer() {
        this(new Random());
    }

    /**
     * Computes an approximate value of Pi via a Monte Carlo method. The method
     * creates {@code samples} random points, computes the percentage of all 
     * points within the radius from the center of the simulated square and 
     * multiplies it by {@code 4.0}.
     * 
     * @param samples the number of points to create.
     * @param radius  the radius of the simulated circle.
     * @return an approximate value of Pi.
     */
    public double computeApproximateValueOfPi(int samples, double radius) {
        Point2D.Double center = new Point2D.Double(radius, radius);
        double squareSideLength = 2.0 * radius;
        long numberOfPointsWithinCircle = 
            IntStream.range(0, samples)
                     .mapToObj(
                             (i) -> { 
                                 return new Point2D.Double(
                                         squareSideLength * random.nextDouble(),
                                         squareSideLength * random.nextDouble()); 
                             })
                     .filter((point) -> { 
                         return point.distance(center) < radius; 
                     }).count();

        return (4.0 * numberOfPointsWithinCircle) / samples;
    }

    /**
     * Computes an approximate value of Pi via a Monte Carlo method with default
     * radius.
     * 
     * @param samples the number of points to create.
     * @return an approximate value of Pi.
     */
    public double computeApproximateValueOfPi(int samples) {
        return computeApproximateValueOfPi(samples, DEFAULT_RADIUS);
    }

    public static void main(String[] args) {
        MonteCarloPiComputer computer = new MonteCarloPiComputer();

        for (int samples = 100_000; samples <= 1_000_000; samples += 100_000) {
            double approximation = 
                    computer.computeApproximateValueOfPi(samples);
            double percentage = approximation / Math.PI;
            System.out.print(String.format("%7d: ", samples));
            System.out.print(String.format("%10f", approximation));
            System.out.println(
                    String.format(
                            ", percentage from exact Pi: %10f", 
                            (100.0 * percentage)));
        }
    }
}

Critique request

I would like to hear any comments and improvement suggestions.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

You have some superfluous imports. I also think that you can rename computeApproximateValueOfPi() to approximatePi() without losing any clarity.

The simulation is complicated by the fact that the circle is centered at an adjustable point (radius, radius). You could just as easily use a unit circle centered at the origin, and generate points in a square with x in the range [0, 1) and y in the range [0, 1). The computation is mathematically equivalent, with less shifting and scaling.

Furthermore, there is no need to instantiate a Point2D for each generated point. You can use .distance(x, y). Better yet, avoid computing the square root by using .distanceSq(x, y).

The output routine in main() could be improved. Your percentage variable doesn't actually store a percentage as its name suggests; the 100× happens in the formatting instead. Splitting up the formatting into several System.out.print() calls defeats the purpose of String.format(). I'd combine them all into one System.out.format() call. Finally, the "percentage from" wording implies that you are calculating the difference; the calculation that you actually performed is what I would call "percentage of".

import java.awt.geom.Point2D;
import java.util.Objects;
import java.util.Random;
import java.util.stream.IntStream;

public class MonteCarloPiComputer {
    /**
     * The random number generator.
     */
    private final Random random;

    public MonteCarloPiComputer(Random random) {
        this.random = Objects.requireNonNull(
            random, 
            "The input random number generator is null."
        );
    }

    public MonteCarloPiComputer() {
        this(new Random());
    }

    /**
     * Computes an approximate value of Pi via a Monte Carlo method. The method
     * creates {@code samples} random points in the upper-right quadrant,
     * computes the fraction of all points within the radius from the origin
     * and multiplies it by {@code 4.0}.
     * 
     * @param samples the number of points to create.
     * @return an approximate value of Pi.
     */
    public double approximatePi(int samples) {
        Random r = this.random;
        Point2D.Double origin = new Point2D.Double();
        long pointsWithinUnitArc = IntStream.range(0, samples)
             .filter(i -> origin.distanceSq(r.nextDouble(), r.nextDouble()) < 1)
             .count();
        return (4.0 * pointsWithinUnitArc) / samples;
    }


    public static void main(String[] args) {
        MonteCarloPiComputer computer = new MonteCarloPiComputer();

        for (int samples = 100_000; samples <= 1_000_000; samples += 100_000) {
            double approximation = computer.approximatePi(samples);
            double pctDiff = 100 * (approximation - Math.PI) / Math.PI;
            System.out.format("%7d: %10f, deviation from exact Pi: %+10f%%%n",
                samples,
                approximation,
                pctDiff
            );
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.