Monte Carlo · for programmers

When the math is too hard, throw dice at it.

Some answers are painful to calculate with a formula. But running the same random experiment ten thousand times? That's just a loop with a counter. That's the entire trick behind Monte Carlo simulation — and you already know how to write it.

Watch the darts find π  ↓
Estimate of π
3.000
Darts thrown
0
01The big idea

Stop solving. Start sampling.

Picture a jar of jellybeans. You could count every bean by hand — slow and error-prone. Or you could grab handfuls at random, look at what you got, and after enough handfuls you'd know the jar pretty well. Monte Carlo is the second approach, turned into code: model the situation, feed it random inputs, run it a huge number of times, and read the answer off the results.

🎲

It uses randomness on purpose

Instead of deriving a clean equation, you let random chance explore the problem for you — one possible outcome at a time.

🔁

Repetition does the heavy lifting

One random trial tells you almost nothing. A million of them, averaged together, tells you almost everything.

📊

You get odds, not just a number

Its real superpower: it hands you a whole range of possible outcomes and how likely each one is — not a single fragile guess.

02The classic example

Estimate π by throwing darts

Here's the famous one. Draw a square, and inside it a quarter-circle. Now throw darts at the square completely at random. The darts that land inside the curve, compared to the total, are directly linked to π. No circle formula required at the throwing stage — just a coin-toss of geometry and a tally.

inside the curve outside the curve
Darts thrown
0
Landed inside
0
π estimate
Off by

Notice how jumpy the estimate is with just a handful of darts — and how it steadies as the count climbs. That's the whole point of the next section.

Why this works (in one breath)

The square has area 1. The quarter-circle inside it has area π/4. Throw darts evenly across the square and the fraction that land inside the curve will hover around π/4. Flip that around and you get your estimate:

π ≈ 4 × (inside ÷ total)

A dart is "inside" when its distance from the corner is ≤ 1 — i.e. x² + y² ≤ 1. That single check is the only geometry you need.

// The entire simulation. Yes, really.
let inside = 0;
const N = 1_000_000;

for (let i = 0; i < N; i++) {
  const x = Math.random();   // 0 … 1
  const y = Math.random();   // 0 … 1
  if (x*x + y*y <= 1) inside++;
}

const pi = 4 * inside / N;
console.log(pi); // ≈ 3.14159…
03Why throwing darts works

More trials, less noise

There's one law doing all the work here, and it has an intimidating name — the Law of Large Numbers — for a simple idea: the more times you repeat a random experiment, the closer your average result lands to the truth. Watch the estimate stagger around early on, then lock onto π.

running estimate true value of π

The catch every programmer should know

Accuracy improves, but slowly. To get one extra digit of precision, you roughly need 100× more trials. That trade-off — easy to set up, expensive to make razor-sharp — is the defining personality of Monte Carlo. It's why it shines for "good enough" answers to messy problems, and struggles when you need many decimal places.

The mental model

Think of each trial as a noisy vote. A few votes can swing wildly. But as votes pile up, the random over- and under-shoots cancel out, and the signal underneath rises to the surface. You're not making any single trial more accurate — you're drowning the randomness in volume.

04The pattern you'll reuse

Four steps, every single time

Strip away the π example and what's left is a recipe that works for almost any uncertain system. Once you see the shape of it, you'll spot Monte Carlo problems everywhere.

STEP 01

Model one trial

Describe a single run of the situation, and pin down exactly where the randomness enters.

STEP 02

Feed it random inputs

Draw random values for the uncertain parts, from distributions that match reality.

STEP 03

Repeat a lot

Loop thousands or millions of times, recording the outcome of each run as you go.

STEP 04

Aggregate

Average them, count the proportion, or plot the spread. That collection is your answer.

function monteCarlo(runs) {
  const results = [];

  for (let i = 0; i < runs; i++) {
    const outcome = simulateOneTrial();  // steps 1 & 2
    results.push(outcome);                // step 3
  }

  return summarise(results);            // step 4: avg, %, histogram…
}
05A real one

Will the project ship on time?

π is cute, but here's where Monte Carlo earns its keep. You've got five chunks of work, each with a best case, likely case, and worst case. Adding up the "likely" numbers gives one tidy estimate — and it lies to you, because work rarely all goes to plan at once. Instead, simulate the whole project thousands of times with random task durations and look at the spread of outcomes.

Each bar = how often, out of all the simulated projects, the total landed in that range of days. The red line is your deadline.

Design & specs2 · 4 · 8 d
Backend5 · 9 · 18 d
Frontend4 · 7 · 14 d
Integration & QA3 · 6 · 15 d
The unknown unknowns1 · 3 · 10 d
Chance on time
Typical (p50)
"Safe" bet (p90)
Sum of likelies
29 d

Drag the deadline after running — the odds update instantly off the same simulated outcomes.

The naïve "add up the likely numbers" answer is 29 days. Run the simulation and you'll usually find the odds of actually hitting that are uncomfortably low. That gap is risk you couldn't see — and the reason people reach for Monte Carlo.

06Knowing when to reach for it

A great tool, not a magic one

Perfect when…

  • The system has real randomness or uncertainty baked in.
  • A clean formula is hard, impossible, or just not worth deriving.
  • Simulating one outcome is easy, even if the whole thing is complex.
  • You want a range of outcomes and their odds — risk, not just a point estimate.

! Watch out when…

  • You need many digits of precision — the cost balloons fast.
  • Your random model doesn't match reality. Garbage in, garbage out.
  • A direct formula already exists and runs instantly. Use that.
  • Compute is tight — millions of runs aren't free.
That's the whole idea

If you can simulate one outcome, you can estimate anything.

Loop it. Count it. Average it. You walked in knowing how to write a loop and a counter — and that was always the hard part. The rest is just letting randomness do the exploring while repetition sharpens the answer.