When a number is hard to calculate exactly, Monte Carlo simulation generates lots of random examples and lets the pattern in them reveal it. Here the number is .
Throw darts uniformly at random into the unit square, and . A dart lands inside the quarter circle of radius when . The fraction of darts that land inside settles toward the quarter circle's share of the square's area — and from that fraction you can estimate .
Task: write estimate_pi(seed, checkpoints), which throws darts one at a time and returns the running estimate of at each checkpoint.
rng = random.Random(seed). For each dart, call rng.random() twice: the first value is , the second is .checkpoints is a list of dart counts in increasing order, e.g. [10, 100, 1000]. At each checkpoint, report the estimate based on every dart thrown so far.Watch the estimates as the checkpoints grow. Their drift toward is the Law of Large Numbers at work, and how slowly they get there — the error shrinks only like — is why Monte Carlo needs so many samples.