9
\$\begingroup\$

I wrote some code that uses Monte Carlo Integration to Approximate pi in Java and Akka. The tl;dr explanation is you can imagine throwing darts at a square with a circle inscribed inside of it. You know the area formulas for a square and a circle so you can use the ratio of darts that landed in the circle vs those that landed in the square to reconstruct pi.

Config.java

package populate;

/**
 * Global class used to change world parameters.
 * At the moment, you can only change the number of actors
 * in the world.
 */
public class Config {
    public static int ACTOR_COUNT = 20;
}

Dart.java

package populate;

import akka.actor.UntypedActor;
import java.util.ArrayList;

public class Dart extends UntypedActor {

    // Location of darts thrown.
    private ArrayList<Point> darts;

    /**
     * Throws a bunch of "darts" at a square.
     * @param size  The number of darts thrown.
     */
    private void throwDarts(int size) {
        for (int i = 0; i < size; i++) {
            darts.add(Point.genRandPoint());
        }
    }

    /**
     * Use Monte Carlo integration to approximate pi.
     * (see: https://www.wikiwand.com/en/Monte_Carlo_integration)
     */
    private float approximatePi() {
        int total = 0; // Keep track of total points thrown.
        int inside = 0; // Keep track of points inside the circle.
        for (Point d : darts) {
            if (d.x * d.x + d.y * d.y <= 1) {
                inside += 1;
            }
            total += 1;
        }
        return 4 * ((float) inside) / total;
    }

    @Override
    public void onReceive(Object msg) {
        if (msg != null) {
            this.throwDarts(50);
            getSender().tell(approximatePi(), getSelf());
        } else {
            getContext().stop(getSelf());
            unhandled(msg);
        }
    }

    @Override
    public void preStart() {
        darts = new ArrayList<Point>();
    }
}

Main.java

package populate;

public class Main {

    public static void main(String[] args) {
        akka.Main.main(new String[] { World.class.getName() });
    }

}

Point.java

package populate;

import java.util.concurrent.ThreadLocalRandom;

public class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    static public Point genRandPoint() {
        return new Point(
                ThreadLocalRandom
                .current()
                .nextDouble(-1, 1),
                ThreadLocalRandom
                .current()
                .nextDouble(-1, 1)
              );
    }
}

World.java

package populate;

import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;

/**
 * This class is responsible for creating the "dart-throwing" actors.
 * It is also responsible for averaging the results of the Dart actors.
 */
public class World extends UntypedActor {

    // Sum of the Dart Actor results.
    private static double sum = 0.0;
    // Keep track of actors still going.
    private static Integer actorsLeft = Config.ACTOR_COUNT;

    /**
     * Calculate the "average of the averages".
     * Each Dart actor approximates pi. This function takes those results
     * and averages them to get a *hopefully* better approximation of pi.
     */
    public void onReceive(Object msg) {
        if (msg != null) {
            sum += (Float) msg;
            actorsLeft--;
            if (actorsLeft <= 0) {
                System.out.println(sum / (double) Config.ACTOR_COUNT);
                getContext().stop(getSelf());
            }
        } else {
            unhandled(msg);
        }
    }

    /**
     * Initializes a number of darts and tells the Dart actors
     * and tells them to start computing.
     */
    @Override
    public void preStart() {
        for (int i = 0; i < Config.ACTOR_COUNT; i++) {
            final ActorRef dart = getContext()
                .actorOf(
                    Props.create(Dart.class), 
                    "dart" + Integer.toString(i));
            // The choice of "0" is used, but anything non-null would
            // work. (If it were null, the Dart actor would die before
            // it did any work.
            dart.tell(0, getSelf());
        }
    }
}

The full repo can be found here and can be run with gradle run.

I don't feel very confident with my understanding of Akka...

\$\endgroup\$
1
  • \$\begingroup\$ Small thing not worth an answer: Don't write a config class: you already have a config file from akka, you can use that instead and access it "for free" through akka utilities \$\endgroup\$ May 3, 2017 at 13:33

1 Answer 1

3
\$\begingroup\$

Naming

The Dart class does not define darts. It holds a Collection of Points called darts. It is a Quiver. I would rename Point as Dart however, and it could extends Java's Point class.

World encompasses a lot of things. Maybe a more precise name would be better? How about DartThrowingCompetition?

Using Streams

Streams are compact, and high-performance. If you're throwing a lot of darts, they're worth a look.

For example:

private void throwDarts(int size) {
    for (int i = 0; i < size; i++) {
        darts.add(Point.genRandPoint());
    }
}

Can become:

private void throwDarts(int size) {
    darts = IntStream.range(0, size).mapToObj(num -> Point::genRandPoint).collect(Collectors.toList());
}

The next bit may be Off-Topic, I got carried away in my thought process :)

But I would even keep darts a Stream to perform the rest of the computations. This is so concise that I compiled it in a single method in the following PiEvaluator class:

public class PiEvaluator {

    /**
     * Throws a bunch of "darts" at a square.
     * @param size  The number of darts thrown.
     * @return the approximation of PI
     */
    private double evaluatePi(int nbDarts) {
        long hitCount = IntStream.range(1, nbDarts)
                                 .mapToObj(num -> TestStream.throwOneDart())
                                 .filter(hit -> hit==true)
                                 .count();
        return 4 * (double) hitCount / nbDarts;
    }

    /**
     * Throws one dart at random. If the dart hits the target, return true.
     * @return true if the dart lands on target
     */
    public boolean throwOneDart(){
        Point dart = Point.genRandPoint();
        return dart.x * dart.x + dart.y * dart.y <= 1;
    }
}

However I'm afraid I didn't use the akka framework at all (I'm not familiar with it) so this might be off-topic. Knowing Streams are lazy, and will wait for the last meaningful call to do the work, I'm not even sure it's akka-able.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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