Chapter 6 · Part 3

Capstone: estimate π

Time to use everything at once on something genuinely fun: estimating π by throwing random darts. It's a Monte Carlo simulation — solving a math problem with randomness — and it touches every skill from this course: array creation, math, boolean masks and aggregation.

The idea

Picture a 1×1 square with a quarter-circle of radius 1 drawn inside it. The square has area 1; the quarter-circle has area π/4. So if you scatter random points across the square, the fraction that land inside the circle is about π/4 — which means:

The whole thing, vectorized

No loops. We generate all the points at once, test them all at once with a boolean mask, and average:

pi.py
import numpy as np

rng = np.random.default_rng(42)
n = 1_000_000

points = rng.random((n, 2))                 # n random (x, y) in the unit square
inside = points[:, 0]**2 + points[:, 1]**2 <= 1.0   # boolean mask: inside the circle?
pi = 4 * inside.mean()                       # fraction inside, times 4

print(f"pi is about {pi:.4f}")
pi is about 3.1422

Read what each line does — it's the whole course in four lines: rng.random((n, 2)) creates an array, **2 and + are vectorized math, <= 1.0 is a boolean mask, and .mean() is an aggregation. inside.mean() works because True counts as 1 and False as 0, so the mean is the fraction that landed inside.

More darts, better estimate

The beauty of Monte Carlo: throw more darts, get closer to the truth. Try a ladder of sizes:

converge.py
for n in [1_000, 10_000, 100_000, 1_000_000]:
  points = rng.random((n, 2))
  inside = points[:, 0]**2 + points[:, 1]**2 <= 1.0
  print(f"n={n:>9,}  pi ~ {4 * inside.mean():.4f}")
n=    1,000  pi ~ 3.0840
n=   10,000  pi ~ 3.1584
n=  100,000  pi ~ 3.1470
n=1,000,000  pi ~ 3.1422

Watch the error shrink — off by 0.058, then 0.017, 0.005, 0.0006. Because it's random, any single run wobbles, but on average more darts means a tighter estimate. You just approximated one of math's most famous constants with a boolean mask and a mean.

Where NumPy goes from here

That four-line simulation is the same shape as serious scientific computing — generate data, transform it with vectorized math, reduce it to an answer. From here:

  • pandas builds spreadsheet-like tables on top of NumPy arrays — the next step for real data.
  • Matplotlib plots arrays; try scattering your dart points colored by inside/outside.
  • The rest of this track uses NumPy everywhere: it's the retriever's math in Semantic Search, and it is the whole engine in A Neural Net From Scratch, where these same operations — matrix math, masks, reshaping — train a real network.

You didn't just read about NumPy — you indexed, broadcast, aggregated and simulated your way through it. Every data and AI tool you pick up next will feel a little more familiar.