Calculating PI using the Monte Carlo method using F#

by Call me dave... 29. March 2009 20:09

Earlier in my career I spent a number of years designing electronic circuits using circuit simulation software.  One of the most important steps within the design process is to determine how robust the circuit will be to different scenarios such as temperature changes or to the spread in electrical characteristics in physical components (ie 100 ohms +/- 5%).  One useful design technique is Monte Carlo analysis where all of the simulation parameters such as component values and temperature are randomly changed by a small value and the circuit simulation is run.  The process repeats thousands of times.  The designer is able to use the results to hone into any areas within the design that unstable and either change the design or use more expensive components that have a tighter tolerance.

Monte Carlo is used:

  • In finance to complex products such as options
  • In mathematics to perform numerical analysis

Monte Carlo can be used to calculate PI

We can calculate PI by using knowing how to calculate the area of a square and a circle as follows

  1. To calculate the area of a Square {S = Width*Width}
  2. To calculate the are of a Circle {C = PI*Radius*Radius}
  3. If we place a circle inside a square Width = 1, Diameter = 1, Radius = 0.5 
  4. Therefore S = 1
  5. Therefore C = PI * 0.5 * 0.5 = PI * 0.25 = PI/4
  6. The Ratio is area between the Circle and the Square is Ratio = C/S = PI/4/1 = PI/4
  7. Hence PI = Ratio * 4

So now we can find PI by knowing there is a relationship between the area of the circle and a square.  Using Monte Carlo Analysis we randomly pick points within the square and check if they also fall into the circle.

  1. Randomly select a Point in the Square P = (X, Y) where X is between 0..1 AND Y is between 0..1
  2. Using the Pythagorean theorem P is in the circle if X*X + Y*Y <= R*R; X*X + Y*Y <= 0.25
  3. Ratio = Points in Circle/All Points

Here is the code

   1: let r = new Random()
   2:  
   3: let inUnitCircle _ =
   4:   let y = 0.5 - r.NextDouble()
   5:   let x = 0.5 - r.NextDouble()
   6:   match y * y + x * x <= 0.25 with
   7:     |true -> 1
   8:     |_ -> 0
   9:  
  10: let rec calculate_pi_using_monte_carlo(numInterations:int)= 
  11:   let numInCircle = List.sum_by (inUnitCircle)[1 .. numInterations]
  12:   4.0 * (double)numInCircle / (double)numInterations 

The unit test is utilises the very useful AreApproximatelyEqual method from mbUnit.  As the number of iterations (random samples) increases the calculated PI starts moving closer and closer to the that of Math.PI.

   1: [<Test>]
   2: let row() =
   3:   let rowVal = [(100, 0.2); (1000,0.07); (3000,0.03)]
   4:   List.iter (fun (i, d)-> Assert.AreApproximatelyEqual (Math.PI, calculate_pi_using_monte_carlo i, d)) rowVal

Unfortunately I was unable to use  [<Row([|(100, 0.2); (1000,0.07); (3000,0.03)|])>] syntax to utilise the Row attribute within F# since the compiler seems to be unable to create arrays of type object.  I would be very interested if anyone has been unable to get it to work.

Tags:

Comments are closed