# Monte Carlo Pi

```
Mix.install([
{:vega_lite, "~> 0.1.6"},
{:kino_vega_lite, "~> 0.1.10"}
])
```

## Introduction

This notebook uses Monte Carlo simulation to approximate π.

By placing 2d points randomly witnin -1 to 1 on each axis and looking at the fraction that land within the unit circle, one can approximate π. The more points, the better the approximation.

Given $A*{sqr}$ being the area of the square and $A*{circ}$ being the area of the unit circle, we have that

$
\frac{A*{circ}}{A*{sqr}} = \frac{\pi r^2}{4r^2} = \frac{\pi}{4}
$

or

$
\pi = 4 \cdot \frac{A*{circ}}{A*{sqr}}
$

A longer description can be found here.

**Note:** The outcome is determined by chance and the quality of the employed random number generator.

## Configuration

`step_count = 2_000`

## Simulation

Produce coordinates:

```
samples =
1..step_count
|> Enum.map(fn _ -> %{x: 2 * :rand.uniform_real() - 1, y: 2 * :rand.uniform_real() - 1} end)
```

Map coordinates to whether they are inside of the unit circle:

```
outcomes =
samples
|> Enum.map(fn sample -> :math.sqrt(sample.x * sample.x + sample.y * sample.y) < 1 end)
```

Calculate the series of approximations:

```
approximation =
outcomes
|> List.foldl([], fn outcome, acc ->
{count, inside, outside} =
case acc do
[last | _] -> {Map.get(last, "count"), Map.get(last, "inside"), Map.get(last, "outside")}
_ -> {0, 0, 0}
end
{inside, outside} =
case outcome do
true -> {inside + 1, outside}
false -> {inside, outside + 1}
end
entry = %{
"count" => count + 1,
"inside" => inside,
"outside" => outside,
"pi" => inside / (count + 1) * 4,
"legend" => "simulated"
}
[entry | acc]
end)
|> Enum.reverse()
```

## Result

The final approximation is:

```
approximation
|> Enum.at(-1)
|> (fn entry -> Map.get(entry, "pi") end).()
```

## Simulation Set

```
defmodule Canvas do
@dim 320
@unit 256
@point_r 2
@gap 8
@gridcolor "#999999"
def render(samples) do
grid_lines = grid()
circle_lines = circle()
point_lines = point(samples)
"""
#{grid_lines}
#{circle_lines}
#{point_lines}
"""
|> Kino.Image.new(:svg)
end
defp grid() do
dashes = "stroke-dasharray=\"3\""
"""
1.0
0.5
0.0
-0.5
-1.0
-1.0
-0.5
0.0
0.5
1.0
"""
end
defp circle() do
"""
"""
end
defp point(samples) do
samples
|> Enum.map(fn sample ->
color =
if :math.sqrt(sample.x * sample.x + sample.y * sample.y) > 1 do
"#ff0000"
else
"#0000ff"
end
"""
"""
end)
|> Enum.join("\n")
end
end
```

`Canvas.render(samples)`

## Path of Approximation

`alias VegaLite, as: Vl`

```
correct = [
%{"legend" => "correct", "pi" => :math.pi(), "count" => 1},
%{"legend" => "correct", "pi" => :math.pi(), "count" => step_count}
]
```

```
Vl.new(width: 400, height: 300)
|> Vl.data_from_values(correct ++ approximation)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "count", type: :quantitative, title: "Step count")
|> Vl.encode_field(:y, "pi", type: :quantitative, title: "Value")
|> Vl.encode_field(:color, "legend", type: :nominal, title: "Legend")
```

## Final Words

In this example, the full unit square is used as the space the randomly positioned points are placed in. We could have restricted this further to the first quadrant.