14. Probability Distributions
Level: Intermediate You'll use this for: modeling request arrivals, error rates, time between failures, and measurement noise.
The key distributions, and when each shows up
| Distribution | Models | Real example |
|---|---|---|
| Bernoulli | one yes/no trial | did this single request fail? |
| Binomial | count of successes in n independent trials |
how many of 1000 requests failed? |
| Poisson | count of events in a fixed time window, at a known average rate | requests arriving per second at an API |
| Normal (Gaussian) | sum of many small independent effects | measurement noise, human heights, aggregated latencies |
| Exponential | time between events in a Poisson process | time until the next server failure |
Binomial
P(k successes in n trials) = C(n,k) · p^k · (1-p)^(n-k)
from scipy.stats import binom
# 1000 requests, each independently has 0.1% error rate — P(exactly 3 errors)?
binom.pmf(3, n=1000, p=0.001)
A small binomial computed fully by hand — 5 coin flips (p=0.5),
what's P(exactly 2 heads)?
C(5,2) = 5! / (2!·3!) = 120 / (2×6) = 10 <- how many ways to choose which 2 of 5 flips are heads (Ch. 15)
p^k = 0.5² = 0.25 <- probability those 2 specific flips are heads
(1-p)^(n-k) = 0.5³ = 0.125 <- probability the other 3 specific flips are tails
P(exactly 2 heads) = 10 × 0.25 × 0.125 = 0.3125
Every binomial probability is this same three-part product: "how many arrangements achieve this outcome" × "probability of the success pattern" × "probability of the failure pattern."
Poisson — the request-arrival workhorse
Models "how many events happen in a fixed window" when events happen
independently at a constant average rate λ.
P(k events) = (λ^k · e^-λ) / k!
from scipy.stats import poisson
# API averages 100 requests/sec. What's P(more than 130 arrive in one second)?
1 - poisson.cdf(130, mu=100)
A small Poisson computed by hand — a server averages λ=2 crashes per
week, what's P(exactly 3 crashes this week)?
λ^k = 2³ = 8
e^-λ = e^-2 ≈ 0.1353 <- a fixed constant once λ is fixed, from a table or calculator
k! = 3! = 3×2×1 = 6
P(3 crashes) = (8 × 0.1353) / 6 = 1.0824 / 6 ≈ 0.1804
Same shape every time: raise λ to the k, multiply by the constant
e^-λ, divide by k! to account for the fact that the k events could
have landed in any order within the window.
Why this matters directly for capacity planning: if you provision for exactly the average load, you will be under capacity roughly half the time, because real traffic follows a Poisson-like distribution with variance, not a flat line. This is the mathematical justification for "provision for p99 load, not average load" (Ch. 25 builds directly on this with queueing theory).
Normal distribution and the Central Limit Theorem
The Central Limit Theorem says: the sum (or average) of many independent random variables tends toward a normal distribution, regardless of the shape of the original distribution. This is why so many real-world measurements (and so many statistical tests, Ch. 22) assume normality — it's not an arbitrary choice, it's a consequence of aggregating many small independent effects.
Exponential distribution — time between failures
If failures follow a Poisson process (constant hazard rate), the time between failures follows an exponential distribution. This is the basis of MTBF (Mean Time Between Failures) reasoning in reliability engineering.
from scipy.stats import expon
# Mean time between failures = 720 hours. P(next failure within 100 hours)?
expon.cdf(100, scale=720)
PMF, PDF, CDF: three ways to describe a distribution
PMF (discrete): p(x) = P(X = x) Σₓ p(x) = 1
PDF (continuous): f(x), P(a ≤ X ≤ b) = ∫ₐᵇ f(x) dx ∫ f(x) dx = 1
CDF (both): F(x) = P(X ≤ x) F'(x) = f(x)
| Symbol | Say it as | Meaning |
|---|---|---|
p(x) |
"probability mass at x" | a real probability, used for countable outcomes (errors, packets, retries) |
f(x) |
"probability density at x" | not a probability. It's probability per unit of x, and can exceed 1 |
F(x) |
"cumulative distribution" | the fraction of outcomes at or below x, always rising from 0 to 1 |
F'(x) = f(x) |
the Fundamental Theorem of Calculus (Ch. 12): the PDF is the CDF's derivative |
The CDF is the one engineers already use: a percentile is the inverse
CDF. "p99 latency = 250 ms" means F(250 ms) = 0.99.
The normal density, every symbol explained
f(x) = 1/(σ·√(2π)) · e^( -(x - μ)² / (2σ²) )
| Piece | What it does |
|---|---|
μ (mu) |
the mean: where the peak sits |
σ (sigma) |
the standard deviation: how wide the bell is |
(x - μ)² |
squared distance from the centre. Symmetric, so the curve is too |
/(2σ²) |
measures that distance in units of σ |
e^(-…) |
turns distance into a weight: 1 at the centre, falling very fast in the tails |
1/(σ√(2π)) |
the normalizer that makes the total area exactly 1, using the Gaussian integral √π (Ch. 12) |
Example: API latency ~ N(μ = 100 ms, σ = 20 ms). What fraction of
requests exceed 140 ms?
z = (140 - 100) / 20 = 2 -> P(Z > 2) = 1 - Φ(2) ≈ 0.0228 (2.3%)
Φ (capital phi) is the standard normal CDF. You look it up in a table or
call scipy.stats.norm.cdf(2). The e^(-x²) shape decides how quickly
tails die. At 4σ the probability is 0.003%, and at 6σ it's one in a billion.
Real latency is not normal (see heavy tails below), and that difference is
where outages come from.
Every distribution's mean and variance at a glance
| Distribution | Parameters | E[X] |
Var(X) |
Engineering example |
|---|---|---|---|---|
| Bernoulli | p |
p |
p(1-p) |
one request fails or not |
| Binomial | n, p |
np |
np(1-p) |
errors in n requests |
| Geometric | p |
1/p |
(1-p)/p² |
attempts until first success (Ch. 8) |
| Poisson | λ |
λ |
λ |
arrivals per second |
| Uniform | a, b |
(a+b)/2 |
(b-a)²/12 |
random backoff jitter in [a, b] |
| Exponential | λ |
1/λ |
1/λ² |
time between arrivals or failures |
| Normal | μ, σ² |
μ |
σ² |
aggregated measurement noise |
Notice the Poisson's mean equals its variance. That gives you a free diagnostic. If your "requests per second" metric has a variance much larger than its mean (overdispersion), arrivals aren't independent. That points to bursty clients, retry storms, or cron jobs firing on the minute.
The Poisson limit theorem: where Poisson comes from
Theorem. If
n → ∞andp → 0withn·p = λheld fixed, thenBinomial(n, p) → Poisson(λ).
Many independent chances, each tiny, give Poisson. That's why it models requests (millions of users, each rarely clicking), disk failures (many disks, each rarely failing) and radioactive decay alike.
Example: 1000 requests at a 0.1% error rate, so λ = 1000·0.001 = 1.
Binomial: P(exactly 3) = C(1000,3)·0.001³·0.999⁹⁹⁷ = 0.06128
Poisson: P(exactly 3) = 1³·e⁻¹/3! = 0.06131
The two agree to three decimal places, and the Poisson version can be worked out by hand.
Memorylessness: the exponential distribution's strange property
P(T > s + t | T > s) = P(T > t)
Proof in one line, using P(T > t) = e^(-λt) from the CDF:
P(T > s+t | T > s) = P(T > s+t) / P(T > s) = e^(-λ(s+t)) / e^(-λs) = e^(-λt)
If failures are exponential, a server that has run for 1,000 hours is exactly as likely to fail in the next hour as a brand-new one. That's why time-based replacement of exponentially-failing parts is pointless. It's also why "it's been quiet for an hour, so a request must be due" is the gambler's fallacy again. Real hardware isn't fully memoryless. Disks follow a "bathtub curve" with high infant mortality, a flat middle and a wear-out phase, and Backblaze's data shows exactly that shape.
The Law of Large Numbers and the Central Limit Theorem, stated properly
Let X₁, …, Xₙ be independent draws from any distribution with mean μ and
standard deviation σ, and let X̄ₙ = (X₁ + … + Xₙ)/n be their average.
Law of Large Numbers.
X̄ₙ → μasn → ∞. Averages settle on the true mean.
Central Limit Theorem. For large
n,(X̄ₙ - μ) / (σ / √n) ≈ N(0, 1)Averages are approximately normal, centred on
μ, with spreadσ/√n, whatever the shape of the original distribution.
| Symbol | Meaning |
|---|---|
X̄ₙ |
the sample mean of n observations |
σ/√n |
the standard error: how much the sample mean itself jitters |
√n |
the price of precision. 4× the data halves the error |
N(0, 1) |
the standard normal: mean 0, standard deviation 1 |
Example: individual request latencies are exponential with mean 50 ms,
so σ = 50 ms as well. That's strongly skewed and nothing like a bell. Now
average them in batches of 100:
standard error = 50 / √100 = 5 ms
95% of batch averages fall in 50 ± 1.96·5 = [40.2, 59.8] ms
A simulation of 100,000 such batches gives a mean of 49.99, a standard deviation of 5.01, and 95.0% inside that interval. The skew has disappeared, as the theorem promises.
import numpy as np
rng = np.random.default_rng(0)
batch_means = rng.exponential(scale=50, size=(100_000, 100)).mean(axis=1)
print(batch_means.mean(), batch_means.std()) # ~50.0, ~5.0
print(np.mean(np.abs(batch_means - 50) < 1.96 * 5)) # ~0.95
The CLT is why A/B tests (Ch. 22) can use normal-based z-tests on
conversion rates, and why mini-batch gradients in ML are noisy estimates of
the true gradient whose noise shrinks like 1/√batch_size. Doubling the batch
cuts gradient noise by only about 30%, which is why "just use bigger batches"
has diminishing returns.
Concentration inequalities: guarantees without assuming normality
The CLT says "approximately, for large n". Sometimes you need a
guarantee that holds for any distribution. These three inequalities give
one:
Markov: P(X ≥ a) ≤ E[X] / a (X ≥ 0)
Chebyshev: P(|X - μ| ≥ kσ) ≤ 1 / k²
Hoeffding: P(|X̄ₙ - μ| ≥ ε) ≤ 2·e^(-2nε²) (each Xᵢ in [0, 1])
| Symbol | Meaning |
|---|---|
a |
a threshold you're worried about |
k |
how many standard deviations out |
ε |
"epsilon", the error tolerance you'll accept on an average |
n |
number of independent samples |
Markov example: average queue length is 4. Without knowing anything else,
P(queue ≥ 40) ≤ 4/40 = 10%. It's a crude bound, but it holds for any
non-negative quantity.
Chebyshev example: for any distribution, at most 1/9 ≈ 11% of
values lie beyond 3σ. The normal distribution puts only 0.3% out there, so a
"3-sigma alert" on non-normal data can fire about 40× more often than you
designed for.
Hoeffding, in AI: how big should an eval set be? You want the model's
measured accuracy within ε = 2 points of the truth, with 95% confidence
(failure probability δ = 0.05). Set 2e^(-2nε²) = δ and solve:
n ≥ ln(2/δ) / (2ε²) = ln(40) / (2 · 0.02²) = 3.689 / 0.0008 ≈ 4,612 examples
Run it the other way and a 500-question benchmark only pins accuracy to
ε = √(ln 40 / 1000) ≈ ±6 points. A leaderboard gap of 3 points on such a
benchmark is noise. Hoeffding is also the backbone of PAC learning theory,
the formal answer to "how much data do I need to learn?"
Heavy tails and the "tail at scale"
Many systems quantities follow a Pareto (power-law) distribution, where the tail decays polynomially instead of exponentially:
P(X > x) = (x_m / x)^α for x ≥ x_m
| Symbol | Meaning |
|---|---|
x_m |
the minimum value (the scale) |
α |
tail index. Smaller is heavier. α ≤ 2 means infinite variance, α ≤ 1 means infinite mean |
With α ≈ 1.16 you get the "80/20 rule": 20% of the flows carry 80% of the
bytes. Internet flow sizes, file sizes and request costs all look like this.
In networking these are the "elephant" flows (few, huge) and "mice" flows (many,
tiny), and datacenter switches schedule them differently.
Tail latency compounds under fan-out. A search query fans out to 100 leaf servers and must wait for the slowest. If each leaf is slow (above its p99) independently 1% of the time:
P(query is slow) = 1 - P(all 100 fast) = 1 - 0.99¹⁰⁰ ≈ 63%
One leaf's p99 becomes the whole service's median. This is the central result of Google's "The Tail at Scale" (Dean & Barroso, 2013). It's why hedged requests (send a backup after the p95 has passed) and tied requests exist, and why p99.9 matters more than p50 in microservice architectures.
AI in the wild: sampling text is sampling a distribution
At every step an LLM outputs logits z, turns them into a categorical
distribution with softmax (Ch. 13), and draws a random sample from it.
Temperature T reshapes that distribution before sampling:
pᵢ = e^(zᵢ / T) / Σⱼ e^(zⱼ / T)
Logits z = (2.0, 1.0, 0.1) for three candidate tokens:
| Temperature | p(token 1) | p(token 2) | p(token 3) | Behaviour |
|---|---|---|---|---|
T = 0.5 |
0.864 | 0.117 | 0.019 | sharp, near-deterministic |
T = 1.0 |
0.659 | 0.242 | 0.099 | the model's raw beliefs |
T = 2.0 |
0.502 | 0.304 | 0.194 | flat, more "creative" and more errors |
As T → 0 this becomes argmax (greedy decoding). As T → ∞ it becomes
uniform. Top-p (nucleus) sampling truncates to the smallest set of
tokens whose probabilities sum to p (say 0.9), which is just the CDF from
earlier in this chapter applied to the sorted probabilities.
Real-life engineering ties
- SWE/SRE: error-budget math, capacity planning against percentile — not average — load (Ch. 9, 25).
- AI: assumed noise models in regression (Gaussian noise), Poisson regression for count data (e.g., predicting clicks/purchases).
- Network: packet arrival modeling (Poisson), queueing theory inputs (Ch. 25 uses this directly — M/M/1 queues assume Poisson arrivals and exponential service times).
- Security: modeling time-between-attacks/scans for anomaly baselining; Poisson-based alerting ("we normally see ~5 failed logins/hour — 200 in the last hour is statistically extreme," tying back to Ch. 9's 3-sigma idea).
More real-life examples:
- Telephone exchanges — and modern call-center staffing tools — use the Erlang C formula, built on the same Poisson-arrival assumption as this chapter, to compute how many agents are needed to hit a target wait-time SLA; the unit "Erlang" is named for the mathematician who solved this exact problem in 1917.
- Backblaze's publicly published hard-drive failure statistics report Annualized Failure Rate, used with an exponential (constant hazard rate) model to estimate the probability a specific drive fails within the next year — the same math as the MTBF example above, with real, public data.
- Insurance actuaries model claim frequency with Poisson distributions and claim severity with heavy-tailed distributions (log-normal or Pareto) — nearly identical in structure to the risk model in Ch. 26.
- A/B testing platforms model each user's conversion as a Bernoulli trial and the aggregate conversion count as Binomial — the statistical foundation underneath every "statistically significant" banner those tools show.
Common mistake
Applying Normal-distribution intuition (symmetric, thin tails) to data that's actually heavy-tailed (network latency, financial returns, security incident severity) — heavy-tailed data has "black swan" events far more often than a Normal model predicts, badly underestimating tail risk.