Where π comes from
Let the lines be a distance t apart and the needle have length ℓ ≤ t. Two random numbers describe a drop: the distance D from the needle's center to the nearest line, uniform on [0, t/2], and the acute angle θ between needle and lines, uniform on [0, π/2]. The needle reaches the line when D ≤ (ℓ/2)·sin θ. Averaging that chance over the angle:
π enters through the angle: the needle points in every direction equally, and the average of sin θ over directions is 2/π. With H crossings in n drops, solving for π gives the estimator
Because π̂ divides by a random count, its error is not simply binomial. The delta method gives SE(π̂) ≈ π̂·√((1 − p̂)/H) with p̂ = H/n. Even with the longest needle allowed here (ℓ = t) the standard error is about 2.37/√n, against 1.64/√n for the random points in a square method — Buffon's experiment is the less efficient way to get π. Shorter needles cross less often and make it worse; drag ℓ/t down and watch the band widen.
Does the simulation cheat by using π?
A real needle needs no π: gravity and a spin do the randomizing. A computer has to generate the direction, and the obvious way — θ = π·U — uses the value of π to estimate π. The estimate is still a fair statistical experiment, but it is circular as a way of discovering π. The Needle direction control offers both options:
- Point in half-disc (default). Draw (u, v) uniformly in the rectangle [−1, 1) × [0, 1) and reject it until it falls inside the unit half-disc. The direction of an accepted point is uniform on [0, π) by rotational symmetry, and sin θ = v/√(u² + v²) needs only a square root. No value of π goes into placing a needle; the code uses π only afterwards, to report the error.
- Angle θ ~ U[0, π). The textbook shortcut, with π supplied by the programmer.
The two give statistically identical results. There is a twist: the rejection step accepts a point with probability π/4, so the computer is quietly running the dartboard estimate of π just to point the needles.
Real needles, real experiments
Georges-Louis Leclerc, Comte de Buffon, posed the problem in 1733 and published the solution in 1777. In the nineteenth century several people dropped physical needles to test it. The best-known result is Mario Lazzarini's from 1901: with ℓ/t = 5/6 he reported 1,808 crossings in 3,408 drops, which gives 355/113 ≈ 3.1415929, correct to six decimal places. At that sample size the standard error is about 0.05, so an error below 10−6 is not believable. The counts give it away: 3,408 = 16 × 213 and 1,808 = 16 × 113, exactly the multiples that turn 2ℓn/(tH) into 355/113 — the result of stopping, or selecting, at a lucky moment. Run the lab with 3,400 needles and a few seeds to see what honest scatter looks like.
Example result
With ℓ/t = 0.8, 5,000 needles and seed 42, 2,540 needles crossed a line — a fraction of 0.5080 against the theoretical 0.5093. That gives π̂ = 3.1496, an error of 0.0080 with a standard error of 0.0438.
| Crossing needles H | 2,540 |
|---|---|
| Fraction crossing H/n | 0.5080 |
| Theory 2ℓ/(πt) | 0.5093 |
| Estimate of π | 3.1496 |
| Error |π̂ − π| | 0.0080 |
| Standard error | 0.0438 |
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.
// Drop one needle: centre uniform on the floor, direction uniform on [0, π).
// 'disc' draws the direction by rejection from the upper half of the unit disc,
// so π never appears in the code; 'angle' draws θ directly and needs π to do so.
export function dropNeedle(len, method, rng) {
const x = FLOOR_W * rng.uniform();
const y = STRIPS * rng.uniform();
let c, s;
if (method === 'angle') {
const theta = Math.PI * rng.uniform();
c = Math.cos(theta);
s = Math.sin(theta);
} else {
let u, v, r2;
do {
u = 2 * rng.uniform() - 1;
v = rng.uniform();
r2 = u * u + v * v;
} while (r2 > 1 || r2 === 0);
const r = Math.sqrt(r2);
c = u / r;
s = v / r;
}
// The needle spans y ± (ℓ/2)·sin θ; it crosses a line when both ends lie in different strips.
const h = (len / 2) * s;
const hit = Math.floor(y - h) !== Math.floor(y + h);
return { x, y, c, s, hit };
}
// P(cross) = 2ℓ/(πt), so π ≈ 2ℓn/(tH). Delta-method standard error: π̂·√((1 − p̂)/H).
export function summarize(len, n, hits) {
const p = hits / n;
const estimate = hits > 0 ? (2 * len * n) / hits : null;
return {
needles: n,
crossings: hits,
cross_fraction: p,
cross_theory: (2 * len) / Math.PI,
estimate,
error: estimate === null ? null : Math.abs(estimate - Math.PI),
std_error: estimate === null ? null : estimate * Math.sqrt((1 - p) / hits),
};
}def drop_needle(length, method, rng):
"""Centre uniform on the floor, direction uniform on [0, pi). Returns True if it crosses a line."""
x = FLOOR_W * rng.uniform() # horizontal position: irrelevant to crossing, kept for drawing
y = STRIPS * rng.uniform()
if method == "angle":
s = math.sin(math.pi * rng.uniform())
else:
# rejection from the upper half of the unit disc: no pi needed
while True:
u = 2 * rng.uniform() - 1
v = rng.uniform()
r2 = u * u + v * v
if not (r2 > 1 or r2 == 0):
break
s = v / math.sqrt(r2)
h = length / 2 * s
return math.floor(y - h) != math.floor(y + h)
def estimate_pi(length, n, hits):
"""P(cross) = 2l/(pi t) with t = 1, so pi ~ 2 l n / H; delta-method standard error."""
p = hits / n
est = 2 * length * n / hits
return est, est * math.sqrt((1 - p) / hits)Run the full model from a terminal:
node simulation.js --needle-length 0.8 --n 5000 --seed 42
python simulation.py --needle-length 0.8 --n 5000 --seed 42Download the model
| File | Contents | Size |
|---|---|---|
README.md | How to run it, parameters, example result | 2.3 KB |
model.json | Parameters with ranges and defaults, outputs, the seed-42 example | 2.0 KB |
simulation.js | The model used on this page plus a command-line runner (Node 18+) | 7.4 KB |
simulation.py | Python 3.9+ port, standard library only | 6.5 KB |
example.csv | Result table from the example run | 5.4 KB |
LICENSE.txt | MIT License | 1.0 KB |
Assumptions & limitations
- Short needles only. The formula 2ℓ/(πt) holds for ℓ ≤ t. Longer needles can cross several lines and need a different formula, so the lab caps ℓ at t.
- Uniform position and direction. The center is uniform over the floor and the direction uniform over half a turn, independently. Real throws are only approximately like this; bias in either breaks the formula.
- Thin, straight needles and exact lines. Needles have no width, and a needle that exactly touches a line is an event of probability zero.
- Delta-method error. The standard error linearizes 1/H; it is poor when there are only a few crossings, and there is no estimate at all when H = 0.
- Pseudo-random numbers. Drops come from a seeded generator; see pseudo-random numbers and seeds.
References
- Grinstead, C. M. & Snell, J. L. Introduction to Probability, 2nd rev. ed., chapter 2. American Mathematical Society. math.dartmouth.edu/~prob/prob/prob.pdf
- 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