Monte Carlo Simulation

From Rosetta Code

Jump to: navigation, search

Programming Task
This is a programming task. It lays out a problem which Rosetta Code users are encouraged to solve, using languages they know.

Code examples should be formatted along the lines of one of the existing prototypes.
A Monte Carlo Simulation is a way of approximating the value of a function where calculating the actual value is difficult or impossible. It uses random sampling to define constraints on the value and then makes a sort of "best guess."

A simple Monte Carlo Simulation can be used to calculate the value for pi. If you had a circle and a square where the length of a side of the square was the same as the diameter of the circle, the ratio of the area of the circle to the area of the square would be pi/4. So, if you put this circle inside the square and select many random points inside the square, the number of points inside the circle divided by the number of points inside the square and the circle would be approximately pi/4.

Write a function to run a simulation like this with a variable number of random points to select. Also, show the results of a few different sample sizes.

Contents

[edit] Ada

 
with Ada.Text_IO;                use Ada.Text_IO;
with Ada.Numerics.Float_Random;  use Ada.Numerics.Float_Random;
 
procedure Test_Monte_Carlo is
   Dice : Generator;
 
   function Pi (Throws : Positive) return Float is
      Inside : Natural := 0;
   begin
      for Throw in 1..Throws loop
         if Random (Dice) ** 2 + Random (Dice) ** 2 <= 1.0 then
            Inside := Inside + 1;
         end if;
      end loop;
      return 4.0 * Float (Inside) / Float (Throws);
   end Pi;
begin
   Put_Line ("     10_000:" & Float'Image (Pi (     10_000)));
   Put_Line ("    100_000:" & Float'Image (Pi (    100_000)));
   Put_Line ("  1_000_000:" & Float'Image (Pi (  1_000_000)));
   Put_Line (" 10_000_000:" & Float'Image (Pi ( 10_000_000)));
   Put_Line ("100_000_000:" & Float'Image (Pi (100_000_000)));
end Test_Monte_Carlo;
 

The implementation uses built-in uniformly distributed on [0,1] random numbers. Note that the accuracy of the result depends on the quality of the pseudo random generator: its circle length and correlation to the function being simulated. Sample output:

     10_000: 3.13920E+00
    100_000: 3.14684E+00
  1_000_000: 3.14197E+00
 10_000_000: 3.14215E+00
100_000_000: 3.14151E+00

[edit] BASIC

Works with: QuickBasic version 4.5

Translation of: Java

DECLARE FUNCTION getPi! (throws!)
CLS
PRINT getPi(10000)
PRINT getPi(100000)
PRINT getPi(1000000)
PRINT getPi(10000000)
 
FUNCTION getPi (throws)
	inCircle = 0
		FOR i = 1 TO throws
			'a square with a side of length 2 centered at 0 has
			'x and y range of -1 to 1
			randX = (RND * 2) - 1'range -1 to 1
			randY = (RND * 2) - 1'range -1 to 1
			'distance from (0,0) = sqrt((x-0)^2+(y-0)^2)
			dist = SQR(randX ^ 2 + randY ^ 2)
			IF dist < 1 THEN 'circle with diameter of 2 has radius of 1
				inCircle = inCircle + 1
			END IF
		NEXT i
	getPi = 4! * inCircle / throws
END FUNCTION

Output:

3.16
3.13648
3.142828
3.141679

[edit] D

 
import std.stdio: writefln;
import std.random: rand;
 
double pi(int nthrows) {
    int inside;
    for (int i; i < nthrows; i++) {
        double r1 = rand() / cast(double)uint.max;
        double r2 = rand() / cast(double)uint.max;
        if (r1*r1 + r2*r2 <= 1.0)
            inside++;
    }
    return 4.0 * inside / nthrows;
}
 
void main() {
    for (int n = 10_000; n <= 100_000_000; n *= 10)
        writefln("%9d: %07f", n, pi(n));
}
 

Sample output:

   100000: 3.137760
  1000000: 3.143508
 10000000: 3.140836
100000000: 3.141666

For much faster results you can use the pseudorandom generator of Tango, or another one like R250-521.

[edit] Forth

Works with: GNU Forth

include random.fs

10000 value r

: hit? ( -- ? )
  r random dup *
  r random dup * +
  r dup * < ;

: sims ( n -- hits )
  0 swap 0 do hit? if 1+ then loop ;
1000 sims 4 * . 3232  ok
10000 sims 4 * . 31448  ok
100000 sims 4 * . 313704  ok
1000000 sims 4 * . 3141224  ok
10000000 sims 4 * . 31409400  ok

[edit] Fortran

Works with: Fortran version 90 and later

MODULE Simulation

  IMPLICIT NONE

  CONTAINS

  FUNCTION Pi(samples)
    REAL :: Pi
    REAL :: coords(2), length
    INTEGER :: i, in_circle, samples
 
    in_circle = 0
    DO i=1, samples
      CALL RANDOM_NUMBER(coords)
      coords = coords * 2 - 1
      length = SQRT(coords(1)*coords(1) + coords(2)*coords(2))
      IF (length <= 1) in_circle = in_circle + 1
    END DO
    Pi = 4.0 * REAL(in_circle) / REAL(samples)
  END FUNCTION Pi

END MODULE Simulation
 
PROGRAM MONTE_CARLO

  USE Simulation 
  
  INTEGER :: n = 10000

  DO WHILE (n <= 100000000)
    WRITE (*,*) n, Pi(n)
    n = n * 10
  END DO
    
END PROGRAM MONTE_CARLO

Output:

       10000     3.12120
      100000     3.13772
     1000000     3.13934
    10000000     3.14114
   100000000     3.14147

[edit] Haskell

import System.Random
import Control.Monad

get_pi throws = do results <- replicateM throws one_trial
                   return (4 * fromIntegral (sum results) / fromIntegral throws)
  where
    one_trial = do rand_x <- randomRIO (-1, 1)
                   rand_y <- randomRIO (-1, 1)
                   let dist = sqrt (rand_x*rand_x + rand_y*rand_y)
                   return (if dist < 1 then 1 else 0)

Example:

Prelude System.Random Control.Monad> get_pi 10000
3.1352
Prelude System.Random Control.Monad> get_pi 100000
3.15184
Prelude System.Random Control.Monad> get_pi 1000000
*** Exception: stack overflow

[edit] Java

public class MC {
	public static void main(String[] args) {
		System.out.println(getPi(10000));
		System.out.println(getPi(100000));
		System.out.println(getPi(1000000));
		System.out.println(getPi(10000000));
		System.out.println(getPi(100000000));
 
	}
	public static double getPi(int numThrows){
		int inCircle= 0;
		for(int i= 0;i < numThrows;i++){
			//a square with a side of length 2 centered at 0 has 
			//x and y range of -1 to 1
			double randX= (Math.random() * 2) - 1;//range -1 to 1
			double randY= (Math.random() * 2) - 1;//range -1 to 1
			//distance from (0,0) = sqrt((x-0)^2+(y-0)^2)
			double dist= Math.sqrt(randX * randX + randY * randY);
			if(dist < 1){//circle with diameter of 2 has radius of 1
				inCircle++;
			}
		}
		return 4.0 * inCircle / numThrows;
	}
}

Output:

3.1396
3.14256
3.141516
3.1418692
3.14168604

[edit] MAXScript

fn monteCarlo iterations =
(
    radius = 1.0
    pointsInCircle = 0
    for i in 1 to iterations do
    (
        testPoint = [(random -radius radius), (random -radius radius)]
        if length testPoint <= radius then
        (
            pointsInCircle += 1
        )
    )
    4.0 * pointsInCircle / iterations
)

[edit] OCaml

let get_pi throws =
  let rec helper i count =
    if i = throws then count
    else
      let rand_x = Random.float 2.0 -. 1.0
      and rand_y = Random.float 2.0 -. 1.0 in
      let dist = sqrt (rand_x *. rand_x +. rand_y *. rand_y) in
      if dist < 1.0 then
        helper (i+1) (count+1)
      else
        helper (i+1) count
  in float (4 * helper 0 0) /. float throws

Example:

# get_pi 10000;;
- : float = 3.15
# get_pi 100000;;
- : float = 3.13272
# get_pi 1000000;;
- : float = 3.143808
# get_pi 10000000;;
- : float = 3.1421704
# get_pi 100000000;;
- : float = 3.14153872

[edit] Python

[edit] At the interactive prompt

Python 2.6rc2 (r26rc2:66507, Sep 18 2008, 14:27:33) [MSC v.1500 32 bit (Intel)] on win32 IDLE 2.6rc2

>>> import random, math
>>> throws = 1000
>>> 4.0 * sum(1 for p in xrange(throws)
	      if math.hypot(*[random.random()*2-1
	                    for q in [0,1]]) < 1) / float(throws)
3.1520000000000001
>>> throws = 1000000
>>> 4.0 * sum(1 for p in xrange(throws)
	      if math.hypot(*[random.random()*2-1
	                    for q in [0,1]]) < 1) / float(throws)
3.1396359999999999
>>> throws = 100000000
>>> 4.0 * sum(1 for p in xrange(throws)
	      if math.hypot(*[random.random()*2-1
	                    for q in [0,1]]) < 1) / float(throws)
3.1415666400000002

[edit] As a program using a function

 
from random import random
from math import hypot
try:
    import psyco
    psyco.full()
except:
    pass
 
def pi(nthrows):
    inside = 0
    for i in xrange(nthrows):
        if hypot(random(), random()) < 1:
            inside += 1
    return 4.0 * inside / nthrows
 
for n in [10**4, 10**6, 10**7, 10**8]:
    print "%9d: %07f" % (n, pi(n))
 
Personal tools