Algorithms & Programming

Lesson 6 of 6

Simulation and Random Sampling

Checking probability answers by Monte Carlo, shuffling with Fisher-Yates, sampling a stream with a reservoir, and building one distribution from another by rejection.

Checking an answer by simulation

Many quant coding rounds start with a probability puzzle and end with "now write code to check it." The recipe is the same every time: write one function that plays out a single trial and returns a result, run it nn times, and average.

Take two independent uniforms U,VU, V on [0,1][0,1] and ask for P(UV<1/2)P(UV < 1/2). If U≤1/2U \le 1/2 the product is always below 1/21/2. If U>1/2U > 1/2 you need V<1/(2U)V < 1/(2U). So

P(UV<1/2)=12+∫1/21du2u=1+ln⁡22≈0.8466.P(UV < 1/2) = \frac12 + \int_{1/2}^{1} \frac{du}{2u} = \frac{1 + \ln 2}{2} \approx 0.8466.
import random, math

def trial():
    return random.random() * random.random() < 0.5

random.seed(1)
n = 100_000
p = sum(trial() for _ in range(n)) / n
se = math.sqrt(p * (1 - p) / n)
print(f"{p:.4f} +/- {1.96 * se:.4f}")   # 0.8469 +/- 0.0022

The estimate is only useful with its error bar. A proportion estimated from nn independent trials has standard error p(1−p)/n\sqrt{p(1-p)/n}, which is the s/ns/\sqrt{n} from the Monte Carlo Option Pricing lesson with s2=p(1−p)s^2 = p(1-p). Here that is 0.001140.00114, so a 95% band is about ±0.0022\pm 0.0022, and the exact 0.84660.8466 sits inside it. A claimed answer of 5/6≈0.8335/6 \approx 0.833 looks close, but it is about 12 standard errors below the estimate, so the simulation rules it out.

The error shrinks like 1/n1/\sqrt{n}: four times the trials halves it. Cost is O(n)O(n) times the cost of one trial. In an interview, say both numbers out loud ("100k trials gives me about three decimal places") and fix the seed so the run is reproducible.