OS-001 · Monte Carlo

Monte Carlo π Simulation

Throw random points at a unit square, count the ones that fall inside the quarter circle, multiply the fraction by four. The result approaches π — slowly, and with an error you can predict before you run it.

Interactive model

OS-001 · monte-carlo-pi · seeded
inside x² + y² ≤ 1outside
running estimateπ±2 standard errors
Parameters
Random points drawn in the unit square
Same seed, same run — here, in simulation.js and in simulation.py.
Points
10,000
Inside circle
7,866
Estimate of π
3.1464
Error |π̂ − π|
0.0048
Standard error
0.0164

Example run: default parameters, seed 42.

What the simulation shows

The quarter circle of radius 1 has area π/4; the unit square around it has area 1. A point drawn uniformly from the square lands inside the quarter circle with probability exactly π/4. Counting hits over many points estimates that probability, and four times the estimate is an estimate of π.

Two things are worth watching in the lab:

  • The estimate wanders, then settles. After 100 points it is often off by 0.1 or more. After 10,000 points it is usually within about 0.03. The convergence plot uses a logarithmic x-axis so the whole run is visible.
  • The error follows a known envelope. The dashed band is π ± 2 standard errors for each sample size. For most seeds the running estimate stays inside it most of the time — that is what a standard error means, not a guarantee for any single run.

Press Play to add points in batches, or change the seed to see a different but equally valid run.

How the model works

Each trial draws two independent uniform numbers x, y on [0, 1) and records a hit when x² + y² ≤ 1. The number of hits H after n trials is binomial with success probability p = π/4.

π̂ = 4 · H / n

Because H is binomial, the standard error of the estimate is

SE(π̂) = 4 · √( p(1 − p) / n ) ≈ 1.642 / √n

With p = π/4 ≈ 0.785 the constant is 4·√(0.785 · 0.215) ≈ 1.642. The error shrinks with the square root of the sample size: to gain one more correct decimal place you need about 100 times as many points. That 1/√n rate is the same for every plain Monte Carlo estimator, which is why this toy problem is the standard first example — see how accurate a Monte Carlo estimate is.

The lab reports the standard error computed from the observed hit fraction, the same formula a practitioner would use when the true answer is unknown.

Algorithm

  1. Set H = 0.
  2. Repeat n times: draw x, y ~ U[0, 1); if x² + y² ≤ 1, increment H.
  3. Return π̂ = 4H/n and SE = 4√(p̂(1 − p̂)/n) with p̂ = H/n.

There is no need to take a square root per point: comparing x² + y² with 1 is equivalent to comparing the distance with 1.

Example result

With the default 10,000 points and seed 42, 7,866 points landed inside the quarter circle, giving π̂ = 3.1464. The standard error at this sample size is about 0.0164, so an error of 0.0048 is an ordinary outcome.

Output of monte-carlo-pi with n=10000 seed=42
Points10,000
Inside circle7,866
Estimate of π3.1464
Error |π̂ − π|0.0048
Standard error0.0164

Implementation

The page runs simulation.js from the package below; the Python file is a line-by-line port with the same random-number generator, so a given seed produces the same result in both languages. The excerpts show the core of each.

JavaScript — simulation.js
// Draw n points uniformly in the unit square and count those with x² + y² ≤ 1.
// The quarter circle covers π/4 of the square, so π ≈ 4 · inside / n.
export function samplePoints(k, rng, onPoint) {
  let inside = 0;
  for (let i = 0; i < k; i++) {
    const x = rng.uniform();
    const y = rng.uniform();
    const hit = x * x + y * y <= 1;
    if (hit) inside++;
    if (onPoint) onPoint(x, y, hit);
  }
  return inside;
}

export function summarize(n, inside) {
  const p = inside / n;
  const estimate = 4 * p;
  return {
    points: n,
    inside,
    estimate,
    error: Math.abs(estimate - Math.PI),
    std_error: 4 * Math.sqrt((p * (1 - p)) / n),
  };
}
Python — simulation.py
def estimate_pi(n, rng):
    """Count uniform points in the unit square that land inside the quarter circle."""
    inside = 0
    for _ in range(n):
        x = rng.uniform()
        y = rng.uniform()
        if x * x + y * y <= 1:
            inside += 1
    p = inside / n
    return 4 * p, 4 * math.sqrt(p * (1 - p) / n)

Run the full model from a terminal:

Shell
node simulation.js --n 10000 --seed 42
python simulation.py --n 10000 --seed 42

Download the model

monte-carlo-pi.zip6 files · 9.6 KBDownload .zip
Files in monte-carlo-pi.zip
FileContentsSize
README.mdHow to run it, parameters, example result1.9 KB
model.jsonParameters with ranges and defaults, outputs, the seed-42 example1.2 KB
simulation.jsThe model used on this page plus a command-line runner (Node 18+)6.3 KB
simulation.pyPython 3.9+ port, standard library only5.2 KB
example.csvResult table from the example run5.2 KB
LICENSE.txtMIT License1.0 KB

Assumptions & limitations

  • Pseudo-random, not random. Points come from a seeded pseudo-random generator (xoshiro128**). It is far better than needed here, but a flawed generator can bias Monte Carlo results; see pseudo-random numbers and seeds.
  • 32-bit resolution. Each coordinate has 2³² possible values. That granularity is invisible at the sample sizes in the lab.
  • A teaching method, not a way to compute π. Series and iterative algorithms produce trillions of digits; this estimator gains one digit per 100× more work.
  • The error band is approximate. It uses the normal approximation to the binomial, which is poor for very small n.

References

  1. Metropolis, N. & Ulam, S. (1949). The Monte Carlo Method. Journal of the American Statistical Association, 44(247), 335–341. doi.org/10.1080/01621459.1949.10483310
  2. Owen, A. B. (2013). Monte Carlo theory, methods and examples, chapter 2. Stanford University. artowen.su.domains/mc
  3. Blackman, D. & Vigna, S. xoshiro / xoroshiro generators and the PRNG shootout. prng.di.unimi.it