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...