Probability Theory
Probability theory is the mathematical language of uncertainty — the foundation beneath every algorithm that learns, infers, or decides under incomplete information.
Overview
Probability theory is the rigorous study of randomness. Where most of mathematics deals with what is — fixed, determined, knowable — probability theory deals with what might be, with uncertainty as a first-class citizen. It gives us the tools to reason precisely about coin flips and quantum states, about stock prices and the weather, about whether a medical test is trustworthy and whether a machine-learning model will generalize.
The theory emerged from gambling problems in the 17th century (Fermat and Pascal trading letters about dice), crystallized in the 18th century through the work of Bernoulli and Laplace, and was placed on a firm axiomatic footing by Kolmogorov in 1933. His insight was elegant: probability is a measure — a function that assigns non-negative numbers summing to one to subsets of a sample space. From three axioms, the entire edifice follows.
In this module you will meet the central objects of the theory: random variables (functions that map outcomes to numbers), the distributions that characterize them (Bernoulli, binomial, Gaussian, Poisson and friends), and the key numerical summaries — expectation, variance, covariance. You will then encounter two of the most important theorems in all of mathematics: the Law of Large Numbers (averages converge) and, lurking just beyond, the Central Limit Theorem (averages are Gaussian in the limit). These results are not merely theoretical curiosities; they are the reason sampling works, the reason statistics is possible, and the reason neural networks can be trained.
Throughout, build two complementary intuitions: the frequentist picture (probability as the long-run fraction of times an event occurs in repeated experiments) and the Bayesian picture (probability as a rational agent's degree of belief, updated by evidence). Both are mathematically valid and practically useful. A great probabilist is fluent in both.
Probability theory is the skeleton beneath modern computer science. Machine learning is, at its core, probabilistic inference — a neural network's softmax output is a probability distribution, gradient descent is navigating an expected loss, and regularization is a Bayesian prior. Algorithms like Naive Bayes, Hidden Markov Models, Gaussian Processes, Variational Autoencoders, and Diffusion Models are incomprehensible without probability. Cryptography leans on probabilistic primality testing (Miller-Rabin). Randomized algorithms — quicksort, hash tables, Monte Carlo integration, Las Vegas algorithms — derive their correctness guarantees from probabilistic analysis. Even the analysis of deterministic algorithms often uses probabilistic tools (the probabilistic method in combinatorics, Chernoff bounds for tail concentration).
Beyond CS, probability is the language of science under uncertainty. Quantum mechanics is irreducibly probabilistic. Epidemiology, clinical trials, A/B testing, financial risk models, and physical simulations all rest on the same theoretical foundation you will build here. The beautiful surprise is that this foundation is simple: just three axioms, and everything else — the Law of Large Numbers, Bayes' theorem, the Central Limit Theorem, information theory, statistical mechanics — is a theorem.
Relationships Between Core Probability Concepts
The architecture of probability theory: from the sample space and Kolmogorov's axioms, random variables extract numerical structure, whose distributions encode everything we need. Summaries (expectation, variance) lead to concentration inequalities and convergence theorems. Joint distributions capture dependencies and give rise to conditioning and Bayes' theorem.
The concepts
01Sample Spaces, Events, and the Kolmogorov Axioms
Every probability model starts with three ingredients:
-
Sample space $\Omega$: the set of all possible outcomes of an experiment. For a fair coin, $\Omega = {H, T}$. For two dice, $\Omega = {(i,j) : 1 \le i,j \le 6}$, a set of 36 ordered pairs. For a random real number, $\Omega = \mathbb{R}$.
-
Event: any subset $A \subseteq \Omega$. The event "sum of two dice equals 7" is ${(1,6),(2,5),(3,4),(4,3),(5,2),(6,1)}$.
-
Probability measure $P$: a function from events to $[0,1]$ satisfying Kolmogorov's three axioms:
- Non-negativity: $P(A) \ge 0$ for all events $A$.
- Normalization: $P(\Omega) = 1$.
- Countable additivity: if $A_1, A_2, \ldots$ are pairwise disjoint events, then $P(\bigcup_{i=1}^\infty A_i) = \sum_{i=1}^\infty P(A_i)$.
From these three axioms, everything else follows. For example: $P(\emptyset) = 0$ (the impossible event has probability zero). $P(A^c) = 1 - P(A)$ (complement rule). $P(A \cup B) = P(A) + P(B) - P(A \cap B)$ (inclusion-exclusion). These are all theorems, not additional axioms.
The countable additivity axiom (also called $\sigma$-additivity) is the subtle one — it extends finite additivity to infinite disjoint unions. This is what makes the theory work for continuous distributions, where individual outcomes have probability zero but ranges have positive probability.
Analogy: Think of a probability measure as a way of distributing a total "mass" of 1 across the sample space. Events are regions of the space, and their probability is their share of the total mass.
import itertools
# Sample space: two dice
omega = list(itertools.product(range(1,7), range(1,7)))
def P(event):
"""Uniform probability: |event| / |omega|"""
return len(event) / len(omega)
# Event: sum == 7
sum7 = [(i,j) for i,j in omega if i+j == 7]
print(P(sum7)) # 6/36 = 0.1667
# Complement rule: P(not sum7) = 1 - P(sum7)
print(1 - P(sum7)) # 0.8333
# Inclusion-exclusion: P(A or B)
A = [(i,j) for i,j in omega if i==6] # first die is 6
B = [(i,j) for i,j in omega if j==6] # second die is 6
AorB = list(set(A) | set(B))
print(P(AorB)) # = P(A)+P(B)-P(A∩B) = 11/36
02Conditional Probability and Independence
Conditional probability is how we update beliefs when we learn new information. Given that event $B$ has occurred (with $P(B) > 0$), the probability of $A$ is: $$P(A \mid B) = \frac{P(A \cap B)}{P(B)}$$
Intuition: we restrict the sample space to $B$ and renormalize.
Bayes' Theorem follows immediately from symmetry of the definition: $$P(B \mid A) = \frac{P(A \mid B), P(B)}{P(A)}$$ This is arguably the most important equation in all of data science. It tells you how to update a prior belief $P(B)$ in light of evidence $A$, producing a posterior $P(B \mid A)$.
Example (medical test): A disease affects 1% of the population ($P(D) = 0.01$). A test has 95% sensitivity ($P(\text{pos} \mid D) = 0.95$) and 90% specificity ($P(\text{neg} \mid \text{no D}) = 0.90$, so false-positive rate $= 0.10$). What is $P(D \mid \text{pos})$? $$P(D \mid +) = \frac{0.95 \times 0.01}{0.95 \times 0.01 + 0.10 \times 0.99} \approx 0.0875$$ Only about 9%! Most positive tests are false positives because the disease is rare. This base-rate neglect surprises almost everyone.
Independence: $A$ and $B$ are independent if $P(A \cap B) = P(A),P(B)$, equivalently $P(A \mid B) = P(A)$ — learning $B$ tells us nothing about $A$. Events can be pairwise independent yet not mutually independent (a subtlety that bites students and practitioners alike).
The chain rule: For any events $A_1, \ldots, A_n$: $$P(A_1 \cap A_2 \cap \cdots \cap A_n) = P(A_1),P(A_2 \mid A_1),P(A_3 \mid A_1,A_2)\cdots$$ This is the foundation of Bayesian networks and probabilistic graphical models.
# Bayes' theorem: medical test example
P_D = 0.01 # prior: disease prevalence
P_pos_given_D = 0.95 # sensitivity
P_pos_given_noD = 0.10 # 1 - specificity
# Law of total probability for P(positive)
P_pos = P_pos_given_D * P_D + P_pos_given_noD * (1 - P_D)
# Posterior
P_D_given_pos = (P_pos_given_D * P_D) / P_pos
print(f"P(disease | positive test) = {P_D_given_pos:.4f}")
# 0.0875 — only 8.75%! Most positives are false alarms.
03Random Variables and Probability Distributions
A random variable $X$ is a function $X: \Omega \to \mathbb{R}$ that assigns a real number to each outcome. It is not "variable" in the algebra sense — it is a deterministic function; the randomness comes from the random draw of $\omega \in \Omega$.
The distribution of $X$ is the complete description of the probabilities of all values $X$ can take:
- For a discrete RV: the probability mass function (PMF) $p(x) = P(X = x)$, which must satisfy $p(x) \ge 0$ and $\sum_x p(x) = 1$.
- For a continuous RV: the probability density function (PDF) $f(x) \ge 0$ with $\int_{-\infty}^{\infty} f(x),dx = 1$. Here $P(a \le X \le b) = \int_a^b f(x),dx$. Note: $P(X = x) = 0$ for any single point $x$.
The cumulative distribution function (CDF) $F(x) = P(X \le x)$ works for both discrete and continuous RVs. It is always non-decreasing, right-continuous, with $F(-\infty) = 0$ and $F(\infty) = 1$.
Key insight: Two different random variables on different sample spaces can have the same distribution. The distribution (PMF or PDF) is what we usually care about in practice — not the underlying sample space.
Analogy: The sample space $\Omega$ is like the raw machine code of an experiment. The random variable is a compiler that translates outcomes into numbers you can do arithmetic with. The distribution is the object-level description everyone shares.
import numpy as np
import matplotlib.pyplot as plt
# Discrete RV: roll a fair die
X_values = [1, 2, 3, 4, 5, 6]
pmf = [1/6] * 6 # PMF
cdf = np.cumsum(pmf) # CDF
print("PMF:", pmf)
print("CDF:", cdf)
# Continuous RV: standard normal
from scipy.stats import norm
x = np.linspace(-4, 4, 200)
print(f"P(-1 <= Z <= 1) = {norm.cdf(1) - norm.cdf(-1):.4f}") # 0.6827
print(f"P(-2 <= Z <= 2) = {norm.cdf(2) - norm.cdf(-2):.4f}") # 0.9545
04Common Distributions: Bernoulli and Binomial
Bernoulli($p$): The simplest non-trivial distribution. A single binary trial with success probability $p \in [0,1]$. The RV $X$ takes value 1 (success) with probability $p$ and 0 (failure) with probability $1-p$. $$P(X = k) = p^k (1-p)^{1-k}, \quad k \in {0,1}$$
- $E[X] = p$, $\text{Var}(X) = p(1-p)$.
- Interpretation: a coin flip with bias $p$, a single click or no-click, a bit.
Binomial($n$, $p$): The sum of $n$ independent Bernoulli($p$) trials. $X$ counts the number of successes in $n$ trials. $$P(X = k) = \binom{n}{k} p^k (1-p)^{n-k}, \quad k = 0, 1, \ldots, n$$ The binomial coefficient $\binom{n}{k}$ counts the number of ways to place $k$ successes among $n$ positions; $p^k(1-p)^{n-k}$ is the probability of any specific arrangement.
- $E[X] = np$, $\text{Var}(X) = np(1-p)$.
- Example: 20 students each independently pass an exam with probability 0.7. The number who pass is Binomial(20, 0.7) with mean 14.
Normal approximation: For large $n$, Binomial($n$,$p$) $\approx$ Normal($np$, $np(1-p)$). This is the Central Limit Theorem in action — why proportions are approximately Gaussian in large samples.
from scipy.stats import binom
import numpy as np
# Binomial(20, 0.7): number passing an exam
n, p = 20, 0.7
# PMF: P(X = k) for each k
for k in [10, 14, 15, 20]:
print(f"P(X={k}) = {binom.pmf(k, n, p):.4f}")
# Expected value and variance
print(f"Mean = {binom.mean(n, p)}") # 14.0
print(f"Var = {binom.var(n, p)}") # 4.2
# P(at least 15 pass)
print(f"P(X >= 15) = {1 - binom.cdf(14, n, p):.4f}")
05Common Distributions: Gaussian (Normal)
The Gaussian distribution $\mathcal{N}(\mu, \sigma^2)$ is the most important distribution in all of probability and statistics. Its PDF is: $$f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)$$ where $\mu$ is the mean and $\sigma^2$ is the variance ($\sigma$ is the standard deviation). The "bell curve" shape comes from the squared exponent: probability falls off exponentially fast as you move away from the mean.
Standard normal $Z = \mathcal{N}(0, 1)$: any Gaussian can be standardized as $Z = (X - \mu)/\sigma$. The 68-95-99.7 rule: $P(\mu - \sigma \le X \le \mu + \sigma) \approx 0.6827$; within $2\sigma$, about 95.45%; within $3\sigma$, about 99.73%.
Why Gaussian is everywhere — the Central Limit Theorem: the sum (or average) of many independent, identically distributed random variables with finite mean and variance converges in distribution to a Gaussian, regardless of the original distribution. This is why measurement errors (sums of many small independent perturbations) are Gaussian, why test scores aggregate this way, why returns on diversified portfolios trend Gaussian.
Properties:
- Closed under linear transformations: if $X \sim \mathcal{N}(\mu, \sigma^2)$, then $aX + b \sim \mathcal{N}(a\mu + b, a^2\sigma^2)$.
- Sum of independent Gaussians is Gaussian: $\mathcal{N}(\mu_1, \sigma_1^2) + \mathcal{N}(\mu_2, \sigma_2^2) = \mathcal{N}(\mu_1+\mu_2, \sigma_1^2+\sigma_2^2)$.
- Maximum entropy: among all distributions with given mean and variance, the Gaussian has the highest entropy — it makes the weakest possible assumptions beyond those two numbers.
Caution: Real-world data often has heavier tails than Gaussian (financial returns, insurance claims). The Gaussian is a model, not a law of nature.
from scipy.stats import norm
import numpy as np
mu, sigma = 170, 10 # heights in cm
# 68-95-99.7 rule
print(norm.cdf(mu+sigma, mu, sigma) - norm.cdf(mu-sigma, mu, sigma)) # ~0.6827
print(norm.cdf(mu+2*sigma, mu, sigma) - norm.cdf(mu-2*sigma, mu, sigma)) # ~0.9545
# Standardize: P(X > 185)
z = (185 - mu) / sigma # z = 1.5
print(1 - norm.cdf(z)) # P(Z > 1.5) = 0.0668
# Inverse CDF (quantile): height at 90th percentile
print(norm.ppf(0.90, mu, sigma)) # ~182.8 cm
06Common Distributions: Poisson and Exponential
Poisson($\lambda$): Counts the number of events in a fixed interval when events arrive at a constant average rate $\lambda$ and are independent of each other. Classic examples: phone calls arriving at a call center per hour, mutations per genome per generation, clicks on a website per minute, radioactive decays per second. $$P(X = k) = \frac{\lambda^k e^{-\lambda}}{k!}, \quad k = 0, 1, 2, \ldots$$
- $E[X] = \lambda$, $\text{Var}(X) = \lambda$ (mean equals variance — a hallmark of Poisson).
- Poisson limit theorem: Binomial($n$,$p$) $\to$ Poisson($\lambda = np$) as $n \to \infty$, $p \to 0$ with $np$ fixed. So the Poisson models "rare events in large populations".
Exponential($\lambda$): The waiting time between Poisson events. If events arrive at rate $\lambda$, the time between consecutive events is Exponential($\lambda$): $$f(t) = \lambda e^{-\lambda t}, \quad t \ge 0$$
- $E[T] = 1/\lambda$, $\text{Var}(T) = 1/\lambda^2$.
- Memoryless property: $P(T > s + t \mid T > s) = P(T > t)$. The exponential distribution has no memory of how long you've already waited. The only continuous memoryless distribution. This makes it the building block of queueing theory and reliability analysis.
Geometric distribution (discrete analog): the number of Bernoulli trials until the first success; the only discrete memoryless distribution.
from scipy.stats import poisson, expon
import numpy as np
# Poisson: website gets avg 5 requests/second
lam = 5
print(f"P(0 requests) = {poisson.pmf(0, lam):.4f}") # 0.0067
print(f"P(5 requests) = {poisson.pmf(5, lam):.4f}") # 0.1755
print(f"P(>10 requests)= {1 - poisson.cdf(10, lam):.4f}") # 0.0137
# Exponential: time (sec) between requests
scale = 1/lam # mean = 1/lambda
print(f"Mean wait = {expon.mean(scale=scale):.2f} sec") # 0.20
# Memoryless: P(T > 0.5 | T > 0.3) = P(T > 0.2)
print(f"P(T > 0.2) = {1 - expon.cdf(0.2, scale=scale):.4f}")
07Expectation, Variance, and Moments
The expected value (expectation, mean) $E[X]$ is the probability-weighted average of all values $X$ can take: $$E[X] = \sum_x x \cdot P(X = x) \quad \text{(discrete)}$$ $$E[X] = \int_{-\infty}^\infty x \cdot f(x),dx \quad \text{(continuous)}$$
Think of $E[X]$ as the center of mass of the probability distribution.
Linearity of expectation: $E[aX + bY] = aE[X] + bE[Y]$, for any random variables $X$ and $Y$, regardless of dependence. This is enormously powerful — it lets you compute expectations of sums by summing expectations, even when the summands are correlated.
Classic application: Expected number of fixed points of a random permutation of $n$ elements. Define $X_i = 1$ if element $i$ is a fixed point. $E[X_i] = 1/n$. By linearity, $E[\sum_i X_i] = \sum_i 1/n = 1$. Elegant!
Variance: $\text{Var}(X) = E[(X - E[X])^2] = E[X^2] - (E[X])^2$. It measures spread around the mean. The standard deviation $\sigma = \sqrt{\text{Var}(X)}$ has the same units as $X$.
Key variance rules: $\text{Var}(aX + b) = a^2 \text{Var}(X)$. For independent $X$, $Y$: $\text{Var}(X+Y) = \text{Var}(X) + \text{Var}(Y)$. (This additivity for independent sums is what drives the $\sqrt{n}$ scaling in the CLT.)
Covariance and correlation: $\text{Cov}(X,Y) = E[(X - E[X])(Y - E[Y])] = E[XY] - E[X]E[Y]$. The Pearson correlation $\rho(X,Y) = \text{Cov}(X,Y) / (\sigma_X \sigma_Y) \in [-1, 1]$ is the normalized version. Independent $\Rightarrow$ $\text{Cov} = 0$, but the converse is false in general.
import numpy as np
# Linearity of expectation: fixed-point example
# Random permutation of n=5 elements
n = 5
trial_count = 100_000
fixed_point_counts = []
for _ in range(trial_count):
perm = np.random.permutation(n)
fixed = np.sum(perm == np.arange(n))
fixed_point_counts.append(fixed)
print(f"Simulated E[#fixed points] = {np.mean(fixed_point_counts):.4f}")
# Should be close to 1.0 regardless of n
# Variance additivity (independent RVs)
X = np.random.normal(3, 2, size=100_000) # N(3,4)
Y = np.random.normal(1, 3, size=100_000) # N(1,9)
Z = X + Y
print(f"Var(Z) ≈ {np.var(Z):.2f}, expected 13")
08Joint, Marginal, and Conditional Distributions
When we have two or more random variables, we need to describe their joint behavior.
Joint PMF/PDF: For discrete $(X, Y)$, $p(x,y) = P(X=x, Y=y)$. For continuous, $f(x,y)$ with $\iint f(x,y),dx,dy = 1$.
Marginal distribution: "Summing out" (or integrating out) one variable to get the distribution of the other: $$p_X(x) = \sum_y p(x,y) \quad \text{or} \quad f_X(x) = \int f(x,y),dy$$ The marginal distribution of $X$ describes $X$ alone, ignoring $Y$.
Conditional distribution: Given $X = x$, the conditional distribution of $Y$ is: $$p_{Y|X}(y \mid x) = \frac{p(x,y)}{p_X(x)}$$ This is just Bayes' theorem applied to densities.
Independence of RVs: $X$ and $Y$ are independent iff $p(x,y) = p_X(x) \cdot p_Y(y)$ for all $(x,y)$. The joint distribution factors into the product of the marginals.
Law of total expectation: $E[X] = E[E[X \mid Y]]$. You can compute the expectation of $X$ by first conditioning on $Y$ and then taking the expectation over $Y$. This is a workhorse of computation — it lets you break a hard expectation into easier conditional expectations.
Multivariate Gaussian: The $d$-dimensional Gaussian $\mathcal{N}(\boldsymbol{\mu}, \boldsymbol{\Sigma})$ has mean vector $\boldsymbol{\mu} \in \mathbb{R}^d$ and covariance matrix $\boldsymbol{\Sigma} \in \mathbb{R}^{d \times d}$ (positive semi-definite). All marginals and conditionals of a multivariate Gaussian are themselves Gaussian — a fact that makes Gaussian processes and Kalman filters tractable.
import numpy as np
# Joint distribution of (X, Y): bivariate discrete
# X = first die, Y = second die (independent)
joint = np.full((6, 6), 1/36) # each (i,j) equally likely
# Marginal of X: sum over Y
marginal_X = joint.sum(axis=1) # axis=1 sums over Y
print("Marginal P(X=k):", marginal_X) # all 1/6
# Conditional: P(Y=y | X=1)
x_val = 0 # X=1 (0-indexed)
cond_Y_given_X1 = joint[x_val] / marginal_X[x_val]
print("P(Y|X=1):", cond_Y_given_X1) # all 1/6 (independent)
# Law of total expectation: E[X] = E[E[X | Y]]
# Since independent, E[X | Y=y] = E[X] = 3.5 for all y
print("E[X] =", np.sum((np.arange(1,7)) * marginal_X)) # 3.5
09Law of Large Numbers
The Law of Large Numbers (LLN) formalizes the intuition that averages stabilize as samples grow. Let $X_1, X_2, \ldots$ be i.i.d. (independent, identically distributed) with mean $\mu = E[X_i]$. Define the sample mean $\bar{X}n = \frac{1}{n}\sum{i=1}^n X_i$. Then:
Weak LLN: $\bar{X}_n \xrightarrow{P} \mu$ as $n \to \infty$. That is, for any $\varepsilon > 0$, $P(|\bar{X}_n - \mu| > \varepsilon) \to 0$. (Convergence in probability.)
Strong LLN: $\bar{X}_n \to \mu$ almost surely — the sample path of averages converges with probability 1.
Proof sketch of Weak LLN via Chebyshev's inequality: Chebyshev says $P(|Y - E[Y]| \ge k) \le \text{Var}(Y)/k^2$. Apply with $Y = \bar{X}_n$: $E[\bar{X}_n] = \mu$ and $\text{Var}(\bar{X}_n) = \sigma^2/n$ (by variance additivity for independent sums). So $P(|\bar{X}_n - \mu| \ge \varepsilon) \le \sigma^2/(n\varepsilon^2) \to 0$.
What it means: The sample mean is a consistent estimator of the true mean. With enough data, our empirical estimate will be arbitrarily close to the truth. This is the mathematical foundation of statistics, polling, Monte Carlo simulation (estimate integrals by averaging random samples), and stochastic gradient descent.
What it does NOT say: It says nothing about the rate of convergence (that's the CLT) or about any individual sequence of outcomes (gambler's fallacy: past outcomes don't change future probabilities).
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
# Fair die: true mean = 3.5
rolls = rng.integers(1, 7, size=10_000)
cumulative_means = np.cumsum(rolls) / np.arange(1, 10_001)
# The cumulative mean converges to 3.5
print(f"After 10 rolls: mean = {cumulative_means[9]:.3f}")
print(f"After 100 rolls: mean = {cumulative_means[99]:.3f}")
print(f"After 1000 rolls: mean = {cumulative_means[999]:.3f}")
print(f"After 10000 rolls: mean = {cumulative_means[-1]:.3f}")
# Should see convergence toward 3.5
10Central Limit Theorem
The Central Limit Theorem (CLT) is perhaps the most remarkable theorem in probability. It says that the standardized sum of many i.i.d. random variables converges to the standard normal, regardless of the original distribution (as long as mean $\mu$ and variance $\sigma^2 < \infty$ exist).
Formally: let $X_1, \ldots, X_n$ be i.i.d. with mean $\mu$ and variance $\sigma^2$. Then: $$\sqrt{n},\frac{\bar{X}_n - \mu}{\sigma} \xrightarrow{d} \mathcal{N}(0,1)$$
Equivalently, $\sum_{i=1}^n X_i \approx \mathcal{N}(n\mu,, n\sigma^2)$ for large $n$.
Interpretation: The LLN says the mean converges; the CLT describes how fast and what the fluctuations look like — they are Gaussian with standard deviation $\sigma/\sqrt{n}$. Doubling precision requires $4\times$ more data.
Why it works intuitively: The characteristic function (Fourier transform of the PDF) of a sum of i.i.d. variables is a product of characteristic functions. Near the origin, every characteristic function looks like $e^{i\mu t - \sigma^2 t^2/2 + \ldots}$. After $n$ copies and standardization, higher-order terms vanish and you get exactly the Gaussian characteristic function $e^{-t^2/2}$.
In practice:
- A/B testing: the difference in sample means is approximately Gaussian, justifying z-tests and t-tests.
- Neural network training: weight updates from minibatch SGD are noisy but approximately Gaussian.
- Monte Carlo: the error in estimating an integral by $n$ samples is $O(1/\sqrt{n})$ with Gaussian distribution.
import numpy as np
from scipy import stats
rng = np.random.default_rng(0)
# Start from a HIGHLY non-Gaussian distribution: Exponential(1)
# Mean=1, Var=1. Sum of n such RVs.
n_per_sample = 30
num_samples = 10_000
samples = rng.exponential(scale=1.0, size=(num_samples, n_per_sample))
sample_means = samples.mean(axis=1)
# Standardize
std_means = (sample_means - 1.0) / (1.0 / np.sqrt(n_per_sample))
# Test for normality: KS test against N(0,1)
stat, p = stats.kstest(std_means, 'norm')
print(f"KS p-value = {p:.4f}") # Large p -> not rejected -> looks Gaussian!
print(f"Skewness = {stats.skew(std_means):.4f}") # Should be near 0
11Markov's and Chebyshev's Inequalities
Probability inequalities give tail bounds — upper bounds on how far a random variable can stray from its mean. They are essential for algorithm analysis and proving convergence results.
Markov's Inequality: For any non-negative RV $X$ and $a > 0$: $$P(X \ge a) \le \frac{E[X]}{a}$$ Proof: $E[X] = \int_0^\infty x f(x),dx \ge \int_a^\infty x f(x),dx \ge a \int_a^\infty f(x),dx = a, P(X \ge a)$. Divide by $a$.
This is weak (uses only the mean) but universal.
Chebyshev's Inequality: For any RV $X$ with mean $\mu$ and variance $\sigma^2$, and $k > 0$: $$P(|X - \mu| \ge k\sigma) \le \frac{1}{k^2}$$ Proof: Apply Markov to $(X - \mu)^2$ with $a = k^2\sigma^2$.
So at least $1 - 1/k^2$ of the probability lies within $k$ standard deviations of the mean, for any distribution. For $k=2$: at least 75% within $2\sigma$; for $k=3$: at least 89%. The Gaussian beats these bounds substantially (95.45% and 99.73%), but Chebyshev holds universally.
Chernoff bounds (beyond this module): For sums of independent 0/1 random variables, exponentially tighter bounds are available. These are the tools for analyzing hash tables, randomized algorithms, and boosting in machine learning.
# Chebyshev illustration: compare bound vs. actual for Uniform[0,1]
import numpy as np
from scipy.stats import uniform
# Uniform[0,1]: mean=0.5, var=1/12, std=1/sqrt(12)
mu = 0.5
sigma = (1/12)**0.5
for k in [1, 2, 3]:
cheb_bound = 1 / k**2
# Actual probability for Uniform: P(|X-0.5| >= k*sigma)
lo = mu - k*sigma
hi = mu + k*sigma
# P outside [lo, hi]
outside = max(0, lo) + max(0, 1-hi) # for Uniform[0,1]
print(f"k={k}: Chebyshev <= {cheb_bound:.3f}, Actual = {outside:.4f}")
Key insights
- ✦Probability is a measure — just three axioms (non-negativity, normalization, countable additivity) generate the entire theory.
- ✦A random variable is a function, not a variable; its distribution (PMF or PDF) is what we usually care about, not the underlying sample space.
- ✦Linearity of expectation holds for ALL random variables, independent or not — this single fact powers thousands of elegant combinatorial arguments.
- ✦The Gaussian distribution maximizes entropy given mean and variance; the CLT explains why it appears everywhere as a universal attractor of sums.
- ✦The Poisson distribution models rare events in large populations and is the limit of Binomial(n,p) as n → ∞ with np fixed.
- ✦The Law of Large Numbers guarantees convergence of sample means to the true mean; the CLT describes the Gaussian fluctuations around that convergence at rate 1/√n.
- ✦Bayes' theorem is not a formula to memorize but a worldview: posterior = likelihood × prior / evidence. Base rates matter enormously.
- ✦Independence means the joint distribution factors into a product of marginals — a property that is rarely exactly true but often approximately useful.
Common pitfalls
- ⚠Confusing independence with zero correlation: uncorrelated random variables can be strongly dependent (e.g., X ~ Uniform[-1,1] and Y = X²).
- ⚠Base-rate neglect in Bayes' theorem: ignoring the prior P(D) causes dramatic overestimation of P(disease | positive test) for rare diseases.
- ⚠The gambler's fallacy: past outcomes of independent trials do not affect future probabilities. The LLN says averages converge; it says nothing about individual future outcomes 'compensating' for the past.
- ⚠Misapplying the CLT: it requires finite mean AND variance. Cauchy-distributed quantities have no finite mean; their sample averages do NOT converge to a Gaussian.
- ⚠Treating the PDF value f(x) as a probability: for continuous RVs, P(X = x) = 0 always. Only integrals of the PDF give probabilities.
- ⚠Conflating conditional probability P(A|B) with P(B|A) — the prosecutor's fallacy. P(evidence | innocent) is not P(innocent | evidence).
- ⚠Forgetting that Var(X+Y) = Var(X) + Var(Y) only holds for INDEPENDENT X and Y; in general, you must add the covariance term 2·Cov(X,Y).
Practice
- 01medium
**The Hat-Check Problem (Derangements via Linearity)**: $n$ people check their hats. The hats are returned uniformly at random. Let $X$ be the number of people who get their own hat back. (a) Compute $E[X]$ using linearity of expectation. (b) Show that $E[X] = 1$ for all $n \ge 1$. (c) Using the Poisson limit theorem, argue that $X$ is approximately Poisson(1) for large $n$, and compute $P(X=0)$ from this approximation. Compare with the exact formula $P(\text{no match}) = \sum_{k=0}^n (-1)^k/k! \approx 1/e$.
- 02easy
**Implement a Monte Carlo Integrator**: Write a function `monte_carlo_integrate(f, a, b, n)` that estimates $\int_a^b f(x)\,dx$ by averaging $n$ evaluations of $f$ at uniform random points in $[a,b]$. Use it to estimate $\int_0^1 e^{-x^2}dx$ (the Gaussian integral) and $\pi/4 = \int_0^1 \sqrt{1-x^2}\,dx$. Plot the error as a function of $n$ and verify it decays as $O(1/\sqrt{n})$ as the CLT predicts.
- 03easy
**Prove Chebyshev from Markov**: Starting only from Markov's inequality $P(Y \ge a) \le E[Y]/a$ for non-negative $Y$, derive Chebyshev's inequality $P(|X - \mu| \ge k\sigma) \le 1/k^2$. Be explicit about which substitution you make.
- 04medium
**Poisson Approximation for Birthday Problem**: $n$ people are in a room, each born on one of 365 days uniformly at random. Let $X$ be the number of *birthday pairs* (pairs of people sharing a birthday). (a) Compute $E[X]$ using linearity. (b) For what value of $n$ is $E[X] \approx 1$? (c) Approximate $P(X=0)$ using the Poisson approximation with $\lambda = E[X]$. Compare this to the famous exact answer that $n \approx 23$ gives $P(\text{no shared birthday}) \approx 0.5$.
- 05medium
**Conditional Expectation Tower Property**: Let $X \sim \text{Uniform}\{1,2,3,4,5,6\}$ (die roll) and $Y | X \sim \text{Binomial}(X, 0.5)$ (flip $X$ fair coins, count heads). (a) Find $E[Y|X=k]$ for each $k$. (b) Use the law of total expectation $E[Y] = E[E[Y|X]]$ to find $E[Y]$. (c) Simulate this in Python to verify your answer.
- 06hard
**Maximum Likelihood Estimation**: You observe $n$ i.i.d. flips of a biased coin and see $k$ heads. (a) Write down the likelihood function $L(p) = P(\text{data} | p)$ for parameter $p$ (probability of heads). (b) Maximize $\log L(p)$ to show the MLE is $\hat{p} = k/n$. (c) Show that $\hat{p}$ is an unbiased estimator: $E[\hat{p}] = p$. (d) Compute $\text{Var}(\hat{p})$ and use Chebyshev to bound the probability that $\hat{p}$ is more than $0.1$ away from $p$, for $n = 100$.
Resources
The gold standard for undergraduate probability. Full lecture notes, problem sets, and exams from Dimitri Bertsekas and John Tsitsiklis.
Harvard STAT 110. Exceptionally well-written, with beautiful story proofs and the LOTUS (Law of the Unconscious Statistician). Free PDF and video lectures.
The definitive Bayesian treatment. Demanding but deeply rewarding. Shows probability as an extension of logic.
Andrew Ng's machine learning course. The probability review notes are concise and ML-focused, excellent for applied grounding.
Rigorous treatment bridging probability and statistics at the MIT level.
The most widely used undergraduate probability textbook. Clear, comprehensive, with many solved examples.
Beautiful interactive visualizations of probability and statistics concepts from Brown University.
Complete reference for all standard distributions in Python/SciPy — PMF, PDF, CDF, quantiles, random sampling.
Checkpoints — you can now…
- ✓You can state the three Kolmogorov axioms and derive the complement rule and inclusion-exclusion formula from them without looking anything up.
- ✓You can apply Bayes' theorem to a medical testing or spam filtering scenario and explain why base rates dominate when the event is rare.
- ✓You can write down the PMF for Bernoulli, Binomial, and Poisson distributions and state their means and variances from memory.
- ✓You can explain why the Gaussian distribution is 'universal' via the Central Limit Theorem, and write a simulation that demonstrates CLT convergence starting from a non-Gaussian distribution.
- ✓You can compute marginal and conditional distributions from a given joint distribution, and verify independence by checking whether the joint factors into a product of marginals.
- ✓You can prove the Weak Law of Large Numbers using Chebyshev's inequality, identifying the key step where variance of the sample mean decreases as 1/n.
- ✓You can use linearity of expectation to solve a combinatorial expectation problem (such as expected fixed points of a random permutation) elegantly, without enumerating cases.