Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monte Carlo calculation of Pi in Scala

Suppose I would like to calculate Pi with Monte Carlo simulation as an exercise.

I am writing a function, which picks a point in a square (0, 1), (1, 0) at random and tests if the point is inside the circle.

import scala.math._
import scala.util.Random

def circleTest() = {
  val (x, y) = (Random.nextDouble, Random.nextDouble)
  sqrt(x*x + y*y) <= 1
}

Then I am writing a function, which takes as arguments the test function and the number of trials and returns the fraction of the trials in which the test was found to be true.

def monteCarlo(trials: Int, test: () => Boolean) =
  (1 to trials).map(_ => if (test()) 1 else 0).sum * 1.0 / trials

... and I can calculate Pi

monteCarlo(100000, circleTest) * 4

Now I wonder if monteCarlo function can be improved. How would you write monteCarlo efficient and readable ?

For example, since the number of trials is large is it worth using a view or iterator instead of Range(1, trials) and reduce instead of map and sum ?

like image 477
Michael Avatar asked Sep 03 '14 14:09

Michael


People also ask

How to estimate pi using the Monte Carlo method?

Estimating Pi using the Monte Carlo Method. How to estimate a value of Pi using the Monte Carlo method - generate a large number of random points and see how many fall in the circle enclosed by the unit square. One method to estimate the value of \( \pi \) (3.141592...) is by using a Monte Carlo method.

What is an example of Monte Carlo estimation?

Monte Carlo estimation. Monte Carlo methods are a broad class of computational algorithms that rely on repeated random sampling to obtain numerical results. One of the basic examples of getting started with the Monte Carlo algorithm is the estimation of Pi. Estimation of Pi.

What is Monte Carlo algorithm?

Monte Carlo methods are a broad class of computational algorithms that rely on repeated random sampling to obtain numerical results. One of the basic examples of getting started with the Monte Carlo algorithm is the estimation of Pi.

How to calculate the value of pi using randomization?

In randomized and simulation algorithms like Monte Carlo, the more the number of iterations, the more accurate the result is. Thus, the title is “ Estimating the value of Pi” and not “Calculating the value of Pi”. Below is the algorithm for the method: 1. Initialize circle_points, square_points and interval to 0. 2. Generate random point x. 3.


4 Answers

It's worth noting that Random.nextDouble is side-effecting—when you call it it changes the state of the random number generator. This may not be a concern to you, but since there are already five answers here I figure it won't hurt anything to add one that's purely functional.

First you'll need a random number generation monad implementation. Luckily NICTA provides a really nice one that's integrated with Scalaz. You can use it like this:

import com.nicta.rng._, scalaz._, Scalaz._

val pointInUnitSquare = Rng.choosedouble(0.0, 1.0) zip Rng.choosedouble(0.0, 1.0)

val insideCircle = pointInUnitSquare.map { case (x, y) => x * x + y * y <= 1 }

def mcPi(trials: Int): Rng[Double] =
  EphemeralStream.range(0, trials).foldLeftM(0) {
    case (acc, _) => insideCircle.map(_.fold(1, 0) + acc)
  }.map(_ / trials.toDouble * 4)

And then:

scala> val choosePi = mcPi(10000000)
choosePi: com.nicta.rng.Rng[Double] = com.nicta.rng.Rng$$anon$3@16dd554f

Nothing's been computed yet—we've just built up a computation that will generate our value randomly when executed. Let's just execute it on the spot in the IO monad for the sake of convenience:

scala> choosePi.run.unsafePerformIO
res0: Double = 3.1415628

This won't be the most performant solution, but it's good enough that it may not be a problem for many applications, and the referential transparency may be worth it.

like image 117
Travis Brown Avatar answered Oct 27 '22 13:10

Travis Brown


Stream based version, for another alternative. I think this is quite clear.

def monteCarlo(trials: Int, test: () => Boolean) =
    Stream
      .continually(if (test()) 1.0 else 0.0)
      .take(trials)
      .sum / trials

(the sum isn't specialised for streams but the implementation (in TraversableOnce) just calls foldLeft that is specialised and "allows GC to collect along the way." So the .sum won't force the stream to be evaluated and so won't keep all the trials in memory at once)

like image 3
The Archetypal Paul Avatar answered Oct 27 '22 13:10

The Archetypal Paul


And a foldLeft version, too:

def monteCarloFold(trials: Int, test: () => Boolean) = 
  (1 to trials).foldLeft(0.0d)((s,i) => s + (if (test()) 1.0d else 0.0d)) / trials

This is more memory efficient than the map version in the question.

like image 2
Will Fitzgerald Avatar answered Oct 27 '22 13:10

Will Fitzgerald


Using tail recursion might be an idea:

def recMonteCarlo(trials: Int, currentSum: Double, test:() => Boolean):Double = trials match {
  case 0 => currentSum
  case x => 
    val nextSum = currentSum + (if (test()) 1.0 else 0.0)
    recMonteCarlo(trials-1, nextSum, test)

def monteCarlo(trials: Int, test:() => Boolean) = {
  val monteSum = recMonteCarlo(trials, 0, test)
  monteSum / trials
}
like image 1
Ashalynd Avatar answered Oct 27 '22 13:10

Ashalynd