Latticework

Command Palette

Search for a command to run...

Monte Carlo Simulation

Variance Reduction

18 min

Explanation

Monte Carlo error shrinks with 1/√n — halving your error means quadrupling your sample count, which gets expensive fast. Variance reduction techniques squeeze a more accurate estimate out of the SAME number of samples, without introducing bias.

Antithetic variates is the simplest one: for every random draw z, also use its mirror image -z. Since a standard normal distribution is symmetric around 0, -z is exactly as likely as z — but pairing them means an unusually high draw is automatically balanced by an unusually low one in the same batch, instead of relying on luck to average out over many independent draws.

import random

random.seed(0)
z = random.gauss(0, 1)
pair = [z, -z]   # both equally valid draws from the same distribution
Try it

The mean of an antithetic-paired sample of Z itself is ALWAYS exactly 0, by construction -- that's not the useful part. The technique shines when averaging something NONLINEAR in Z, like the next exercise's exp(Z).

Loading editor…
Explanation

Averaging Z itself with antithetic pairs gives exactly 0 every time — not a useful demonstration on its own, since it doesn't actually reduce variance in anything interesting (the true mean of Z is already 0). The technique earns its keep on a NONLINEAR function of Z, like exp(Z) (closely related to how a stock price is computed from a random shock in GBM) — there, exp(z) and exp(-z) are NOT mirror images of each other, so averaging the pair genuinely pulls the estimate toward the true expected value faster than the same number of fully independent draws would.

# without antithetic variates: n fully independent draws
total = sum(math.exp(random.gauss(0, 1)) for _ in range(n))

# with antithetic variates: n // 2 draws, each contributing TWO terms
total = sum(math.exp(z) + math.exp(-z) for z in [random.gauss(0, 1) for _ in range(n // 2)])

Both use the same total number of exp() evaluations — the antithetic version just tends to converge to the true value with less sample-to- sample noise.

Exercise

Write `antithetic_pairs(n, seed=42)`: generate `n // 2` standard normal draws (`random.gauss(0, 1)`), then return a list of all `n` values — the `n // 2` draws followed by their negatives, each rounded to 6 decimal places, in the order generated.

Exercise

Write `estimate_exp_z_antithetic(n, seed=42)`: estimate E[e^Z] for a standard normal Z using antithetic variates. Generate `n // 2` draws; for each `z`, include both `exp(z)` and `exp(-z)` in the average. Return the result rounded to 4 decimal places. (The true value is e^0.5 ≈ 1.6487.)

Quiz

What's the core idea behind the antithetic variates technique?

Checkpoint

You can generate antithetic variate pairs and use them to reduce variance when estimating the expectation of a nonlinear function of a random variable.