Part IV Theory 3 Foundations

Probability and Information Theory

Every loss function is a probability statement. Every metric is a random variable. Learn the rules once and most of ML stops being magic.

This page is the theory under the models. It covers the rules of probability, the inequalities that bound error, and the limit theorems behind every A/B test. Then it moves to information theory. Entropy, cross-entropy and KL explain why we train with the losses we use. The last sections cover sketches and sampling, the tools you reach for at scale.

Each topic has a plain definition, the math, the intuition, and why it matters on the job. Each ends with short interview checks. For worked puzzles like Bayes on a medical test, see Statistics and Probability. This page does not repeat them.

Contents

  1. Axioms, conditioning and independence
  2. Random variables, expectation and variance
  3. Key inequalities
  4. Laws of large numbers and the CLT
  5. The multivariate Gaussian
  6. Exponential family and sufficient statistics
  7. Markov chains and PageRank
  8. Entropy, cross-entropy and KL divergence
  9. Mutual information
  10. Jensen-Shannon and Wasserstein
  11. Concentration and sketching
  12. Sampling methods

Axioms, conditioning and independence

Probability space. A sample space Ω of outcomes, a set of events, and a measure P on those events. P follows three axioms. Everything else is derived from them.

The three axioms

From these you get the working rules. P(Ac) = 1 − P(A). P(A ∪ B) = P(A) + P(B) − P(A ∩ B). The union bound P(∪ Ai) ≤ ∑ P(Ai) holds with no assumptions. It powers many proofs in learning theory.

Conditional probability

Conditioning shrinks the world to the event B and renormalizes.

P(A | B) = P(A ∩ B) / P(B),      P(B) > 0

Chain rule:   P(A1, …, An) = P(A1) P(A2 | A1) … P(An | A1, …, An−1)
Total prob.:  P(A) = ∑i P(A | Bi) P(Bi)      for a partition {Bi}
Bayes:        P(B | A) = P(A | B) P(B) / P(A)

The chain rule is exact for any order. An autoregressive language model is just the chain rule over tokens. It models P(xt | x<t) and multiplies.

Independence and conditional independence

Neither kind of independence implies the other. Two examples make this clear.

The XOR example also shows pairwise without mutual independence. X, Y and Z are each pairwise independent. But any two of them fix the third.

Conditioning on a collider creates bias. Suppose you only study users who clicked. Clicks depend on both relevance and position. So inside your data, relevance and position look dependent even if they are not. This is selection bias, and it is the same math as the XOR coins.

Why it matters in practice

Interview check

Random variables, expectation and variance

Random variable. A function X from outcomes to numbers. Its distribution is fully described by the CDF F(x) = P(X ≤ x). A discrete X has a PMF p(x). A continuous X has a density f(x) with F(x) = ∫−∞x f(t) dt.

Expectation and variance

E[X]      = ∑x x p(x)         or   ∫ x f(x) dx
E[g(X)]   = ∑x g(x) p(x)      (law of the unconscious statistician)
Var(X)    = E[(X − E X)2] = E[X2] − (E X)2
Cov(X, Y) = E[(X − E X)(Y − E Y)] = E[XY] − E[X] E[Y]
ρ(X, Y)   = Cov(X, Y) / (σX σY)   ∈ [−1, 1]

Expectation is linear with no conditions. E[aX + bY] = a E[X] + b E[Y], even if X and Y are dependent. Variance is not linear.

Var(aX + bY) = a2 Var(X) + b2 Var(Y) + 2ab Cov(X, Y)
Var(wTX)    = wT Σ w           for a random vector X with covariance Σ

The second line explains why every covariance matrix is positive semi-definite. A variance can never be negative, so wTΣw ≥ 0 for every w.

Zero correlation is not independence

Let X be uniform on [−1, 1] and Y = X2. Then Cov(X, Y) = E[X3] = 0. But Y is a function of X. Correlation only sees linear dependence. Independence implies zero covariance, never the reverse. The one big exception is a jointly Gaussian pair.

Averages of correlated variables

Take n variables, each with variance σ2 and pairwise correlation ρ. Their mean has this variance.

Var(X̄) = ρ σ2 + (1 − ρ) σ2 / n

As n grows, the second term goes to zero. The first does not. This one line explains bagging and random forests. Averaging trees kills the (1 − ρ) part. Random feature subsets lower ρ itself.

Law of total expectation and total variance

E[X]   = E[ E[X | Y] ]                                   (tower rule)
Var(X) = E[ Var(X | Y) ]  +  Var( E[X | Y] )
         \___ within ___/     \___ between ___/

Short derivation of the second. Write Var(X) = E[X2] − (E X)2. Apply the tower rule to E[X2] = E[ Var(X|Y) + E[X|Y]2 ]. Then subtract (E[E[X|Y]])2. The last two pieces form Var(E[X|Y]).

The intuition is simple. Total spread splits into spread inside each group plus spread between group means.

Why it matters in practice

Interview check

Key inequalities

Tail bound. An upper limit on how likely a random variable is to land far from where you expect. Stronger assumptions buy tighter bounds.

Markov

For X ≥ 0 and any a > 0, we have P(X ≥ a) ≤ E[X] / a.

Proof in one line. a · 1{X ≥ a} ≤ X pointwise. Take expectations. It needs only a mean and non-negativity. So it is weak but always available.

Chebyshev

Apply Markov to (X − μ)2. For any k > 0 we get P(|X − μ| ≥ kσ) ≤ 1 / k2.

Applied to a sample mean, P(|X̄ − μ| ≥ t) ≤ σ2 / (n t2). That is already a proof of the weak law of large numbers.

Jensen

For convex f, f(E[X]) ≤ E[f(X)]. For concave f the sign flips. Equality holds when X is constant or f is linear where X lives.

x f(x) convex E[f(X)] f(E[X]) Jensen gap x1 x2 E[X]
X takes x1 or x2 with equal odds. The chord midpoint sits above the curve. The vertical gap is E[f(X)] − f(E[X]) ≥ 0.

Jensen is the most used inequality in ML theory.

Hoeffding

Let X1, …, Xn be independent with Xi ∈ [ai, bi]. For any t > 0:

P( |X̄ − E X̄| ≥ t ) ≤ 2 exp( −2 n2 t2 / ∑i (bi − ai)2 )

For Xi ∈ [0, 1]:      P( |X̄ − μ| ≥ t ) ≤ 2 e−2 n t2
Solve for n:          n ≥ ln(2/δ) / (2 t2)

Chebyshev decays like 1/n. Hoeffding decays exponentially in n. The price is bounded variables. The proof bounds the moment generating function, then applies Markov to eλX̄. That trick is the Chernoff method.

A worked number. You want a click rate within ±0.01 with 95% confidence. Hoeffding says n ≥ ln(40) / (2 × 0.0001) ≈ 18,445. That holds for any distribution on [0, 1]. The CLT gives a smaller n, about 9,604 at p = 0.5, but only as an approximation.

From Hoeffding to generalization

Take a finite hypothesis class H and a 0-1 loss. Apply Hoeffding to each h. Then take a union bound over all |H| of them. With probability at least 1 − δ, every h satisfies:

|R(h) − R̂n(h)| ≤ √( (ln|H| + ln(2/δ)) / (2n) )

This is the template for all of learning theory. VC dimension and Rademacher complexity replace ln|H| for infinite classes.

Why it matters in practice

Interview check

Laws of large numbers and the CLT

Limit theorems. The law of large numbers says the sample mean converges to the true mean. The central limit theorem says how the error is shaped on the way there.

Modes of convergence

Almost sure implies in probability. In probability implies in distribution. None of the reverse holds in general.

Precise statements

Let X1, X2, … be i.i.d. with mean μ. Let X̄n be the mean of the first n.

How fast: Berry-Esseen

The CLT is a limit. Berry-Esseen gives a rate. Let ρ = E|X − μ|3. Then:

supx | P( √n (X̄n − μ)/σ ≤ x ) − Φ(x) |  ≤  C ρ / (σ3 √n),     C < 0.48

Skewed data has a large ρ / σ3. So it needs far more samples before normal confidence intervals are honest. Revenue per user is the classic case.

The delta method

Often you need a smooth function of a mean, not the mean itself. If √n (X̄n − μ) → N(0, σ2) and g is differentiable with g′(μ) ≠ 0:

√n ( g(X̄n) − g(μ) )  →  N( 0, g′(μ)2 σ2 )

Ratio metric R = Ȳ / X̄  (clicks per impression, per user):
Var(R) ≈ (1/n) [ Var(Y)/μX2 − 2 μY Cov(X,Y)/μX3 + μY2 Var(X)/μX4 ]
When the CLT fails. A Cauchy variable has no mean. Its sample mean is Cauchy again for every n, so it never settles. Power-law data with tail index below 2 has infinite variance. The mean converges, but slower than 1/√n and not to a Gaussian. Cap, log-transform, or use medians and the bootstrap.

Why it matters in practice

Interview check

The multivariate Gaussian

Multivariate Gaussian. A distribution on vectors in ℝd, fixed by a mean μ and a positive definite covariance Σ. Every linear combination of its coordinates is a 1D Gaussian.

Density

N(x; μ, Σ) = (2π)−d/2 |Σ|−1/2 exp( −½ (x − μ)T Σ−1 (x − μ) )

The quadratic form in the exponent is the squared Mahalanobis distance. Contours of equal density are ellipses. Their axes are the eigenvectors of Σ. Their radii scale with the square roots of its eigenvalues.

The closure properties

Split x into blocks x1 and x2. Split μ and Σ the same way, with blocks Σ11, Σ12, Σ21, Σ22.

Affine:       A x + b        ~ N( Aμ + b,  A Σ AT )
Marginal:     x1             ~ N( μ1,  Σ11 )                      (just read off the block)
Conditional:  x1 | x2        ~ N( μ1|2, Σ1|2 )
              μ1|2 = μ1 + Σ12 Σ22−1 (x2 − μ2)
              Σ1|2 = Σ11 − Σ12 Σ22−1 Σ21         (Schur complement)
Product:      N(x; a, A) N(x; b, B) ∝ N(x; c, C),   C = (A−1 + B−1)−1,  c = C(A−1a + B−1b)

Read the conditional slowly. The new mean is a linear regression of x1 on x2. The coefficient matrix is Σ12Σ22−1. The new covariance does not depend on the observed value x2. Observing data always shrinks the covariance, since the subtracted term is PSD.

In 2D with correlation ρ, this becomes E[x1 | x2] = μ1 + ρ (σ1/σ2)(x2 − μ2). The variance becomes σ12(1 − ρ2). That is regression to the mean in one line.

The precision matrix

Let Λ = Σ−1. A zero in Σ means two coordinates are marginally independent. A zero in Λ means they are independent given all the others. Sparse precision matrices are Gaussian graphical models. The graphical lasso estimates them with an L1 penalty.

For a jointly Gaussian vector, uncorrelated implies independent. This fails if only the marginals are Gaussian. Take X ~ N(0,1) and Y = SX with S a random sign. Y is N(0,1) and uncorrelated with X. But |Y| = |X|.

Why it is everywhere

Sampling and the reparameterization trick

Factor Σ = L LT with Cholesky. Draw ε ~ N(0, I). Then x = μ + Lε has the right distribution, by the affine rule. A VAE uses this to backprop through sampling. The randomness sits in ε, so gradients flow to μ and L.

Why it matters in practice

Interview check

Exponential family and sufficient statistics

Exponential family. Distributions whose log density is linear in a fixed set of statistics T(x). Bernoulli, categorical, Gaussian, Poisson, exponential, gamma, beta and Dirichlet all belong.

Canonical form

p(x | η) = h(x) exp( ηT T(x) − A(η) )

η      natural parameter
T(x)   sufficient statistic
A(η)   log-partition: A(η) = log ∫ h(x) exp(ηT T(x)) dx
h(x)   base measure

The log-partition generates moments

Differentiate A under the integral. You get two facts that drive everything else.

∇A(η)   = E[ T(X) ]
∇2A(η)  = Cov[ T(X) ]   ≽ 0

So A is convex. The log likelihood ηT∑T(xi) − nA(η) is concave in η. MLE has no bad local optima. Set the gradient to zero and you get moment matching.

∇A(η̂) = (1/n) ∑i T(xi)        model moments = data moments

Three members worked out

Sigmoid and softmax are not design choices. They are the mean maps of the Bernoulli and the categorical. A generalized linear model sets η = wTx. The canonical link is the inverse of ∇A. The gradient of the negative log likelihood is then always (prediction − target) · x.

Sufficient statistics

A statistic T(X) is sufficient for θ when the data hold no extra information about θ once you know T. Formally, P(X | T(X), θ) does not depend on θ.

Fisher-Neyman factorization. T is sufficient if and only if p(x | θ) = g(T(x), θ) h(x).

The practical meaning is compression. You can fold a stream of a billion events into a handful of running sums. You can still fit the model exactly.

Conjugate priors and maximum entropy

Every exponential family has a conjugate prior of the form p(η) ∝ exp(ηTχ − νA(η)). The posterior just adds data statistics to χ and counts to ν. Beta-Bernoulli and Dirichlet-categorical are the famous cases.

There is also a dual view. Fix the expected values of T(x). The maximum entropy distribution that meets them is exactly the exponential family with those statistics.

Why it matters in practice

Interview check

Markov chains and PageRank

Markov chain. A random process where the next state depends only on the current state. The past matters only through the present.

Transition matrix and stationary distribution

P(Xt+1 = j | Xt = i, Xt−1, …) = P(Xt+1 = j | Xt = i) = Pij

Each row of P sums to 1.
Distribution after t steps:   πt = π0 Pt
Stationary distribution:     π P = π,    ∑i πi = 1

So π is a left eigenvector of P with eigenvalue 1. A row-stochastic matrix always has one.

A π = 0.22 B π = 0.44 C π = 0.33 0.5 0.5 0.5 0.5 1.0
A three-state chain. Solving πP = π gives π = (2, 4, 3) / 9. B gets the most mass because both A and C feed it.

When the stationary distribution is unique and reached

Detailed balance and MCMC

If πiPij = πjPji for all i and j, then π is stationary. Sum both sides over i to see it. Such chains are called reversible.

Metropolis-Hastings builds a chain with detailed balance for any target p known up to a constant. Propose x′ from q(x′ | x). Accept with probability min(1, p(x′)q(x | x′) / (p(x)q(x′ | x))). The unknown normalizer cancels in the ratio. That is why MCMC works for Bayesian posteriors.

PageRank is a stationary distribution

Model a surfer who follows a random out-link with probability d. Otherwise, the surfer jumps to a random page. The rank of a page is the long-run share of time spent there.

G = d P + (1 − d) (1/n) 1 1T          d ≈ 0.85
PageRank = the π with π G = π

The teleport term makes every entry of G positive. So the chain is irreducible and aperiodic, and π is unique. The second eigenvalue of G is at most d. So power iteration converges like dt. About 50 steps give 4 digits at d = 0.85. Pages with no out-links are set to jump uniformly.

import numpy as np


def pagerank(adj: np.ndarray, d: float = 0.85, tol: float = 1e-10) -> np.ndarray:
    """Power iteration for PageRank. adj[i, j] = 1 if page i links to page j."""
    n = adj.shape[0]
    out = adj.sum(axis=1, keepdims=True)
    # Dangling pages (no out-links) jump uniformly.
    P = np.where(out > 0, adj / np.maximum(out, 1), 1.0 / n)
    G = d * P + (1 - d) / n          # row-stochastic "Google matrix"
    pi = np.full(n, 1.0 / n)
    while True:
        new = pi @ G                 # one step of the chain
        if np.abs(new - pi).sum() < tol:
            return new
        pi = new


adj = np.array([[0, 1, 1, 0],
                [0, 0, 1, 0],
                [1, 0, 0, 0],
                [0, 0, 1, 0]], dtype=float)
pi = pagerank(adj)
print(np.round(pi, 4), round(pi.sum(), 6))
# [0.3725 0.1958 0.3941 0.0375] 1.0

Page 3 has no in-links, so it keeps only the teleport mass (1 − d)/n = 0.0375. Page 2 has three in-links and wins.

Why it matters in practice

Interview check

Entropy, cross-entropy and KL divergence

Entropy. The average surprise of a random outcome. It is also the fewest bits per symbol any code can use, on average, to send draws from that distribution.

Definitions

Entropy:         H(p)     = − ∑x p(x) log p(x)
Cross-entropy:   H(p, q)  = − ∑x p(x) log q(x)
KL divergence:   KL(p‖q) =   ∑x p(x) log( p(x) / q(x) )

Key identity:    H(p, q) = H(p) + KL(p‖q)

Use log base 2 for bits and natural log for nats. The surprise of one outcome is −log p(x). Rare events surprise more.

Cross-entropy loss is maximum likelihood

Let p̂ be the empirical distribution of the training data, with mass 1/n on each xi. Let qθ be the model. Then:

(1/n) ∑i log qθ(xi)  =  ∑x p̂(x) log qθ(x)  =  − H(p̂, qθ)

argmaxθ log-likelihood  =  argminθ H(p̂, qθ)  =  argminθ KL(p̂‖qθ)

The last step holds because H(p̂) does not depend on θ. So three views agree. Maximize likelihood. Minimize cross-entropy. Minimize forward KL from data to model.

For classification the target is one-hot y and the model gives softmax probabilities s = softmax(z).

L(z, y)  = − ∑k yk log sk  =  − zc + log ∑k ezk
∂L/∂zk = sk − yk

The gradient is prediction minus target, the exponential family pattern again. Compute it from logits with logsumexp. Never take log of a softmax output, since it underflows.

import numpy as np


def kl(p: np.ndarray, q: np.ndarray) -> float:
    """KL(p || q) in nats. Assumes q > 0 wherever p > 0."""
    m = p > 0
    return float(np.sum(p[m] * np.log(p[m] / q[m])))


def entropy(p: np.ndarray) -> float:
    m = p > 0
    return float(-np.sum(p[m] * np.log(p[m])))


p = np.array([0.70, 0.20, 0.10])
q = np.array([0.40, 0.40, 0.20])
cross = float(-np.sum(p * np.log(q)))
print(f"H(p)={entropy(p):.4f}  H(p,q)={cross:.4f}  KL(p||q)={kl(p, q):.4f}")
print(f"H(p,q) - H(p) = {cross - entropy(p):.4f}")
print(f"KL(q||p)={kl(q, p):.4f}  (not equal: KL is asymmetric)")
# H(p)=0.8018  H(p,q)=0.9856  KL(p||q)=0.1838
# H(p,q) - H(p) = 0.1838
# KL(q||p)=0.1920  (not equal: KL is asymmetric)

Forward versus reverse KL

Fit a simple q to a complex p. Which direction you minimize changes the answer a lot.

p (two modes) q from forward KL(p‖q): covers both, mass in the gap q from reverse KL(q‖p): one mode Best single Gaussian q under each direction
Forward KL spreads q across both modes and puts mass where p has almost none. Reverse KL locks onto one mode and ignores the other.

The ELBO ties it together

log p(x) = Eq(z)[ log p(x, z) − log q(z) ]  +  KL( q(z) ‖ p(z | x) )
         = ELBO(q)                             +  (≥ 0)

log p(x) does not depend on q. So raising the ELBO lowers the reverse KL to the true posterior. That is why mean-field VI tends to underestimate posterior variance.

Why it matters in practice

Interview check

Mutual information

Mutual information. How much knowing one variable cuts your uncertainty about another. It is zero exactly when the two are independent.

Definition and identities

I(X; Y) = KL( p(x, y) ‖ p(x) p(y) )
        = H(X) − H(X | Y)
        = H(Y) − H(Y | X)
        = H(X) + H(Y) − H(X, Y)

Feature selection

Information gain in a decision tree is the mutual information between the split and the label. Filter methods rank features by I(Xj; Y). mRMR adds a penalty for redundancy. It picks features with high I(Xj; Y) and low average I(Xj; Xselected).

Estimating MI is the hard part. Binning works in one or two dimensions. The KSG estimator uses nearest neighbours for continuous data. In high dimensions, all estimators have high bias or variance.

InfoNCE and contrastive learning

Take a batch of K pairs (xi, yi) drawn from p(x, y). For each xi, the matching yi is the positive. The other K − 1 are negatives. Score pairs with a critic f, often a scaled cosine similarity.

LInfoNCE = − E[ log ( ef(xi, yi) / ∑j=1K ef(xi, yj) ) ]

I(X; Y)  ≥  log K − LInfoNCE

The loss is cross-entropy for picking the positive out of K. A perfect critic drives the loss to zero. Then the bound reads I ≥ log K. So the bound can never exceed log K. That is one reason contrastive methods want huge batches or memory queues.

Why it matters in practice

Interview check

Jensen-Shannon and Wasserstein

Divergences between distributions. KL can be infinite and is lopsided. Jensen-Shannon fixes the symmetry. Wasserstein measures how far mass must move, so it stays useful when supports do not overlap.

Jensen-Shannon divergence

JS(p, q) = ½ KL(p ‖ m) + ½ KL(q ‖ m),     m = ½(p + q)

0 ≤ JS ≤ log 2,     symmetric,     √JS is a true metric

Because m covers both p and q, JS is always finite. The original GAN links to it directly. With the optimal discriminator, the generator minimizes 2 · JS(pdata, pg) − log 4.

Here is the catch. If pdata and pg sit on disjoint low-dimensional manifolds, JS equals log 2 no matter how close they are. The gradient is zero. Early GAN training often failed this way.

Wasserstein distance

W1(p, q) = infγ ∈ Π(p, q)  E(x, y) ~ γ [ ‖x − y‖ ]                   (earth mover's)
        = sup‖f‖L ≤ 1  Ep[f(X)] − Eq[f(X)]                    (Kantorovich-Rubinstein dual)

In 1D:   W1(p, q) = ∫ | Fp(x) − Fq(x) | dx

Π(p, q) is the set of joint plans whose marginals are p and q. Think of p as piles of dirt and q as holes. W1 is the least total work to fill the holes.

One example shows the difference

Let p be a point mass at 0 and q a point mass at θ.

WGAN uses the dual form. The critic f must be 1-Lipschitz. Weight clipping enforced this at first. A gradient penalty on ‖∇f‖ works better.

Why it matters in practice

Interview check

Concentration and sketching

Sketch. A small summary of a huge stream that answers one kind of query with a provable error bound. Hashing makes the summary random. Concentration inequalities bound the error.

Count-min sketch: frequencies

Keep a d × w table of counters and d independent hash functions h1, …, hd. To add item x, increment cell (r, hr(x)) in every row r. To query x, take the minimum over the d cells.

w = ⌈ e / ε ⌉,      d = ⌈ ln(1/δ) ⌉

true(x)  ≤  estimate(x)  ≤  true(x) + ε N       with probability ≥ 1 − δ

The proof uses only Markov’s inequality. Fix a row. Other items that collide with x add an overcount Z ≥ 0. Each other item lands in x’s cell with probability 1/w. So E[Z] ≤ N/w = εN/e. Markov gives P(Z ≥ εN) ≤ 1/e. The minimum fails only if all d independent rows fail. That has probability at most e−d = δ.

Memory is O((1/ε) log(1/δ)), independent of the number of distinct items. The sketch never undercounts. It works best for heavy hitters, where εN is small next to the true count.

import numpy as np


class CountMin:
    """Count-min sketch. Estimates never undercount. Overcount <= eps * N w.p. 1 - delta."""

    def __init__(self, eps: float = 0.001, delta: float = 0.01, seed: int = 0):
        self.w = int(np.ceil(np.e / eps))         # width
        self.d = int(np.ceil(np.log(1 / delta)))  # depth (number of hash rows)
        self.table = np.zeros((self.d, self.w), dtype=np.int64)
        rng = np.random.default_rng(seed)
        self.salts = rng.integers(1, 2**31, size=self.d)

    def _cols(self, key: int) -> np.ndarray:
        return (key * self.salts + 12345) % 2_147_483_647 % self.w

    def add(self, key: int, count: int = 1) -> None:
        self.table[np.arange(self.d), self._cols(key)] += count

    def query(self, key: int) -> int:
        return int(self.table[np.arange(self.d), self._cols(key)].min())


rng = np.random.default_rng(0)
stream = rng.zipf(1.3, size=200_000)
stream = stream[stream < 10**6]
cm = CountMin()
for k in stream:
    cm.add(int(k))
true = np.bincount(stream)
for k in [1, 2, 10, 500]:
    print(f"key {k:>4}: true {true[k]:>6}  sketch {cm.query(k):>6}")
print(f"memory {cm.d} x {cm.w} counters, bound eps*N = {0.001 * len(stream):.0f}")
# key    1: true  50971  sketch  50972
# key    2: true  20577  sketch  20581
# key   10: true   2556  sketch   2561
# key  500: true     21  sketch     24
# memory 5 x 2719 counters, bound eps*N = 197

HyperLogLog: distinct counts

Hash each item to a uniform bit string. The chance that a hash starts with k zeros is 2−(k+1). Say the longest run of leading zeros so far is k. Then you have likely seen about 2k distinct items. Repeats do not matter, since the same item always gives the same hash.

One such estimate is very noisy. HyperLogLog splits the hash. The first b bits pick one of m = 2b registers. Each register keeps its own max leading-zero count. The estimate combines registers with a harmonic mean and a bias constant.

Two more you should know

Reservoir sampling is the sketch for a uniform sample. See Statistics and Probability for its proof.

Why it matters in practice

Interview check

Sampling methods

Sampling. Turning uniform random numbers into draws from a target distribution, or into estimates of expectations under it.

Inverse CDF

If U ~ Uniform(0, 1) and F is a CDF, then X = F−1(U) has CDF F. The proof is one line. P(F−1(U) ≤ x) = P(U ≤ F(x)) = F(x).

Rejection sampling

Pick a proposal q you can sample and a constant M with p(x) ≤ M q(x) everywhere. Draw x ~ q and u ~ Uniform(0, 1). Keep x if u < p(x) / (M q(x)).

Importance sampling

Estimate an expectation under p with samples from q. Reweight each sample by how much more likely it is under p.

Ep[ f(X) ] = Eq[ f(X) w(X) ],      w(x) = p(x) / q(x)

Plain IS:            μ̂ = (1/n) ∑i f(xi) w(xi)                          unbiased
Self-normalized:     μ̂ = ∑i f(xi) w(xi) / ∑i w(xi)                     biased, consistent
Effective size:      ESS = (∑ wi)2 / ∑ wi2

The best q is proportional to |f(x)| p(x). So put samples where f p is large, not just where p is large. q needs heavier tails than p. Otherwise rare huge weights make the variance blow up. A tiny ESS is the warning sign.

import numpy as np

rng = np.random.default_rng(1)
n = 100_000

# Goal: P(X > 4) for X ~ N(0, 1). True value is about 3.167e-5.
hits = rng.standard_normal(n) > 4
naive = hits.mean()

# Importance sampling: draw from q = N(4, 1), reweight by w = p(x) / q(x).
x = rng.normal(4.0, 1.0, n)
w = np.exp(-4.0 * x + 8.0)          # p(x) / q(x) for these two Gaussians
terms = (x > 4) * w
est = terms.mean()

print(f"naive      : {naive:.3e}  ({hits.sum()} hits)")
print(f"importance : {est:.3e}  rel. std err {terms.std() / np.sqrt(n) / est:.2%}")
# naive      : 2.000e-05  (2 hits)
# importance : 3.162e-05  rel. std err 0.67%

The naive estimate rests on two hits. Shifting the proposal into the tail gives a 0.67% relative error with the same budget.

The Gumbel-max trick

Draw Gk = −log(−log Uk) independently for each class. Then:

argmaxk ( log πk + Gk )  ~  Categorical(π)

Logits z work too:  argmaxk ( zk + Gk )  ~  softmax(z)

Why it works. The max of Gumbels shifted by log πk is again Gumbel. The chance that index k wins is exactly πk / ∑j πj. You never normalize, so it works straight from logits.

import numpy as np

rng = np.random.default_rng(0)
logits = np.array([2.0, 1.0, 0.0, -1.0])
probs = np.exp(logits - logits.max())
probs /= probs.sum()

# Gumbel-max: argmax(logits + Gumbel noise) is an exact draw from softmax(logits).
n = 200_000
g = -np.log(-np.log(rng.uniform(size=(n, logits.size))))
draws = np.argmax(logits + g, axis=1)
freq = np.bincount(draws, minlength=logits.size) / n

print("softmax  :", np.round(probs, 4))
print("gumbel   :", np.round(freq, 4))
# softmax  : [0.6439 0.2369 0.0871 0.0321]
# gumbel   : [0.6436 0.2374 0.0872 0.0318]

Why it matters in practice

Interview check

Recap

← T2 — Statistical Inference T4 — Optimization →