Snapshot / Computational Thinking Repo ↗

Random variables and the bell curve

Roll one die: every face is equally likely — a flat distribution. Roll two and add them: 7 is now far more common than 2 or 12. Add up more random numbers and the histogram of the total grows a smooth, symmetric hump.

This is the Central Limit Theorem, one of the deepest facts in all of probability: the sum (or average) of many independent random variables looks like a bell curve — a normal distribution — almost no matter what you started with. It's why the normal distribution is everywhere, from measurement error to test scores to stock returns.

Below, each "sample" is the sum of k random numbers drawn uniformly from [0,1]. Slide k from 1 upward and watch a flat slab morph into a bell, live in WebAssembly.

begin
    using PlutoUI, WasmMakie
end

numbers added per sample (k) = 1

number of samples = 3000

let
    # One flat loop: draw nsamples*k uniforms as a single stream, and every k of them
    # forms one sample (their sum). Histogram those sums over [0, k]. Keeping it to a
    # single loop is what lets the bonded counts compile cleanly to wasm.
    nbins = 40
    counts = Vector{Float64}(undef, nbins)
    for b in 1:nbins
        counts[b] = 0.0
    end
    s = 20261
    acc = 0.0
    j = 0
    grand = nsamples * k
    lo = 0.0
    hi = Float64(k)
    for t in 1:grand
        s = (s * 16807) % 2147483647
        acc += Float64(s) / 2147483647.0      # one uniform[0,1]
        j += 1
        if j == k                              # one full sample is ready
            frac = (acc - lo) / (hi - lo)
            b = Int64(floor(frac * (nbins - 1))) + 1
            if b < 1
                b = 1
            end
            if b > nbins
                b = nbins
            end
            counts[b] += 1.0
            acc = 0.0
            j = 0
        end
    end
    centers = Vector{Float64}(undef, nbins)
    for b in 1:nbins
        centers[b] = lo + (hi - lo) * (Float64(b) - 0.5) / Float64(nbins)
    end
    fig = Figure(size = (600, 340))
    ax = Axis(fig[1, 1])
    for b in 1:nbins
        lines!(ax, [centers[b], centers[b]], [0.0, counts[b]])   # a histogram bar
    end
    fig
end

At k = 1: each sample is the sum of 1 uniform random numbers, so its average value is k/2 = 0.5. At k = 1 the histogram is flat (a single uniform is equally likely anywhere). By k = 2 it is a triangle, and from about k = 6 it is already an excellent bell curve. That convergence — fast, and from a flat starting shape — is the Central Limit Theorem in action.

Why it matters

The Central Limit Theorem is the reason the normal distribution earns its central place: anything that is the accumulation of many small independent effects ends up bell-shaped. Measurement noise, the heights of people, the average of a survey — all are sums of many little randomnesses, so all are approximately normal.

It also underpins why averaging helps: the average of n measurements has a spread that shrinks like 1/√n, so it clusters ever more tightly around the truth. Estimation, error bars, and confidence intervals all flow from this one theorem.

Appendix

The MIT lecture uses Distributions.jl and Plots.jl. WebAssembly can't run those in the browser, so the random numbers come from an inline Park-Miller generator and the histogram is drawn with WasmMakie. The phenomenon — sums of uniforms converging to a bell — is identical.