Monte Carlo Methods

Some quantities are easy to describe but hard to compute exactly. The Monte Carlo approach is to estimate them by repeated random sampling. The theoretical justification is the Law of Large Numbers: if we can express a quantity as an expectation, then the sample average of repeated random draws converges to that expectation.

The example program for this chapter is in the file 08_monte_carlo.lisp.

A Short History

The idea of using random sampling to estimate mathematical quantities has surprisingly deep roots. In 1777, the French naturalist Georges-Louis Leclerc, Comte de Buffon, posed a problem about dropping a needle onto a floor of parallel lines and computed the probability that the needle crosses a line. His answer involved pi, giving an early example of a randomized geometric estimator for a mathematical constant.

The modern practice of Monte Carlo methods was developed in the 1940s at Los Alamos National Laboratory by Stanislaw Ulam, John von Neumann, and Nicholas Metropolis, in the context of the Manhattan Project. They needed to solve integrals arising in neutron transport that had no analytic solution, and Ulam suggested simulating the neutron trajectories randomly on the ENIAC computer. The name “Monte Carlo” was chosen by Metropolis as a nod to Ulam’s uncle, who often gambled at the Monte Carlo casino in Monaco.

Since then, Monte Carlo methods have become one of the most important computational techniques in all of science. They power simulations in physics, chemistry, biology, finance, engineering, and machine learning. The Markov chain Monte Carlo methods that underlie modern Bayesian statistics are direct descendants of the original Los Alamos work.

The Core Idea

A Monte Carlo estimator works as follows. We want to estimate some quantity Code Test. We find a random variable Code Test whose expected value Code Test equals Code Test. Then we draw many independent samples of Code Test and average them:

math

By the Law of Large Numbers, Code Test converges to Code Test as Code Test grows. The variance of the estimator is Code Test, so the standard error decreases as Code Test. By the Central Limit Theorem, Code Test is approximately normal for large Code Test, so we can construct confidence intervals for Code Test using the normal-based methods from Chapter 7.

The design freedom in Monte Carlo lies in choosing the random variable Code Test. Any random variable whose expectation equals Code Test is a valid estimator, but different choices have different variances and therefore different accuracies for the same sample size. Much of the theory of Monte Carlo is really about finding good Code Test.

Two properties make this estimator trustworthy. It is unbiased: Code Test for every Code Test, so it is centered on the right answer rather than merely approaching it. And because it is unbiased, its mean squared error equals its variance,

math

so the root-mean-square error is exactly the standard error Code Test that the program reports. Reducing error therefore means reducing Code Test, which is the whole point of the variance-reduction techniques below. Not every Monte Carlo estimator is unbiased, though: estimators built as ratios or other nonlinear functions of averages carry a bias of order Code Test, which the Code Test standard error dominates for large Code Test but which matters for small samples.

Monte Carlo Integration

The most common use of the core idea is computing integrals, because any integral is an expectation in disguise. To evaluate

math

write it as Code Test with Code Test, since the uniform density on Code Test is the constant Code Test. The estimator draws Code Test uniformly and averages:

math

More generally, if Code Test is any density we can sample from, then Code Test, estimated by the plain average of Code Test over draws Code Test. The choice of sampling density is where the design freedom lives, because the same integral can be rewritten against any density Code Test that is positive wherever the integrand is nonzero:

math

Sampling from an Code Test concentrated where Code Test is large is exactly the importance sampling we return to below; picking Code Test is the same as picking the estimator’s variance.

Estimating Pi

Our example estimates the value of Code Test. Consider a unit square Code Test (area Code Test) and the quarter disk of radius Code Test centered at the origin (area Code Test). If we throw uniformly random points into the square, the probability that a point lands inside the quarter disk equals the ratio of the areas:

math

Let Code Test if the Code Test-th point lands in the disk, Code Test otherwise. Then the Code Test are i.i.d. Code Test, and by the LLN:

math

So our estimator is:

math
 1 (defun random-point-in-unit-square ()
 2   "Return (x, y) with x,y ~ Uniform(0,1) independently."
 3   (values (random 1.0d0 *rng-state*)
 4           (random 1.0d0 *rng-state*)))
 5 
 6 (defun in-quarter-disk-p (x y)
 7   "Is (x,y) inside the quarter disk of radius 1?
 8    The condition is x^2 + y^2 <= 1."
 9   (<= (+ (* x x) (* y y)) 1.0d0))
10 
11 (defun estimate-pi (n)
12   "Monte Carlo estimate of pi using n random points.
13    Estimator = 4 * (count inside disk) / n."
14   (let ((inside 0))
15     (dotimes (i n)
16       (multiple-value-bind (x y) (random-point-in-unit-square)
17         (when (in-quarter-disk-p x y)
18           (incf inside))))
19     (* 4.0d0 (/ inside n 1.0d0))))

A point is inside the quarter disk if Code Test, which is the Pythagorean distance from the origin. Uniform sampling over the square gives each region a probability equal to its area, which is the key property that makes the ratio of counts equal the ratio of areas.

Standard Error

How accurate is our estimate? For a Code Test variable, the standard error of the sample mean Code Test is Code Test. The estimator is Code Test, so its standard error is:

math

where Code Test is the sample proportion. The program reports both the estimate and the standard error:

 1 (defun estimate-pi-with-se (n)
 2   "Estimate pi and also report a standard error for the estimate."
 3   (let* ((inside 0)
 4          (p-hat 0.0d0)
 5          (estimate 4.0d0)
 6          (se 0.0d0))
 7     (dotimes (i n)
 8       (multiple-value-bind (x y) (random-point-in-unit-square)
 9         (when (in-quarter-disk-p x y)
10           (incf inside))))
11     (setf p-hat (/ inside n 1.0d0))
12     (setf estimate (* 4.0d0 p-hat))
13     (setf se (* 4.0d0 (sqrt (/ (* p-hat (- 1.0d0 p-hat)) n 1.0d0))))
14     (values estimate se)))

By the Central Limit Theorem, an approximate 95% confidence interval for Code Test is Code Test. This lets us report the estimate along with a principled measure of its uncertainty.

Running the Example

 1 === Monte Carlo Estimation of pi ===
 2 True pi = 3.141593
 3 
 4        n     estimate     error    std-error
 5   1000      3.032000  0.109593  0.054175
 6   10000     3.144000  0.002407  0.016405
 7   100000    3.137640  0.003953  0.005202
 8   1000000   3.138252  0.003341  0.001645
 9 
10 Note: the error scales roughly as 1/sqrt(n). Doubling n
11 shrinks the standard error by ~0.707x; halving the error needs 4x n.

With Code Test points, the estimate is off by about Code Test. With Code Test points, the error drops to about Code Test. The standard error column tracks the actual error well: the error is typically within Code Test or Code Test standard errors of the true value. The random state is reseeded on each run, so your exact estimates will differ, while the Code Test shrinkage of the standard error stays the same.

The Cost of Monte Carlo

The standard error decreases as Code Test. This is a slow convergence rate. To halve the error, you need Code Test times as many samples. To reduce the error by a factor of Code Test, you need Code Test times as many samples.

This is the fundamental tradeoff of Monte Carlo methods. They are incredibly general: you can estimate almost anything by random sampling. But they are slow to converge. For problems where exact computation is feasible, you should prefer exact methods. Monte Carlo shines when exact computation is intractable, such as high-dimensional integrals or complex probabilistic models.

An important observation: the Code Test rate does not depend on the dimension of the problem. Classical numerical integration (Simpson’s rule, Gaussian quadrature) has convergence rates that get worse in higher dimensions. In Code Test dimensions, deterministic quadrature typically has an error that scales like Code Test for some fixed Code Test depending on smoothness. When Code Test is large (say, Code Test), this deterministic error decreases painfully slowly with the number of function evaluations. Monte Carlo, in contrast, still has the same Code Test rate regardless of dimension. This is why Monte Carlo dominates in high-dimensional problems like statistical physics, Bayesian inference in complex models, and reinforcement learning.

Variance Reduction

A large amount of Monte Carlo research is devoted to reducing variance without increasing the number of samples. Four common techniques:

Antithetic variates: pair each sample with its “opposite” so that variability partly cancels. For example, when sampling Code Test from Code Test, also use Code Test. If the estimator is monotonic in Code Test, the two are negatively correlated and their average has lower variance.

Control variates: subtract a related quantity whose expectation is known. If we want Code Test and know Code Test exactly, we can estimate Code Test for a well-chosen Code Test. If Code Test and Code Test are correlated, the modified estimator has lower variance.

Importance sampling: sample from a different distribution and reweight. Especially useful when the region of interest (say, a tail event) has low probability under the natural distribution.

Stratified sampling: partition the sample space into strata and sample proportionally from each.

These techniques can dramatically improve Monte Carlo performance in practice, often by a factor of Code Test or more. In our Code Test example, we could use antithetic variates by pairing Code Test with Code Test; the correlation between the two indicator variables would reduce the variance of the estimator.

Quasi-Random Sequences

An alternative to true Monte Carlo is quasi-Monte Carlo, which uses low-discrepancy sequences like Sobol or Halton sequences instead of pseudo-random numbers. These sequences are more evenly spread than random points and can give convergence rates closer to Code Test rather than Code Test for smooth integrands. Quasi-Monte Carlo is widely used in high-dimensional finance and computer graphics, but its analysis is more delicate than classical Monte Carlo, and independence-based tools like the CLT do not apply directly.

Buffon’s Needle: An Older Pi Estimator

Buffon’s original problem was to estimate the probability that a needle of length Code Test dropped onto a floor with parallel lines spaced distance Code Test apart will cross a line. Buffon showed that this probability is Code Test. Rearranging, a Monte Carlo estimate of Code Test from Code Test dropped needles that produce Code Test crossings is:

math

This is an even older and just as valid Monte Carlo estimator for Code Test as the disk method. It has different variance properties and is a nice teaching example.

The program implements both the antithetic-variates trick and Buffon’s needle, then compares three estimators by repeating each one Code Test times and reporting the empirical standard deviation of the estimates:

1 === Variance Reduction and Buffon's Needle ===
2   Comparing estimators over 200 repetitions of 5000 points each:
3     plain disk       mean= 3.1390  empirical SD= 0.0231
4     antithetic disk  mean= 3.1400  empirical SD= 0.0202
5     Buffon's needle  mean= 3.1373  empirical SD= 0.0334

The antithetic estimator has a smaller standard deviation than the plain one at the same cost, so pairing each Code Test with its reflection Code Test genuinely reduces variance (Problem 8.6). Buffon’s needle, using the same number of random draws, has the largest standard deviation of the three: a valid estimator but a less efficient one (Problem 8.4). The exact numbers vary from run to run.

Applications Beyond Pi

Monte Carlo methods are used in physics (particle transport simulations), finance (option pricing), engineering (reliability analysis), and machine learning (Bayesian inference, reinforcement learning). The basic idea is always the same: express the quantity of interest as an expectation, then estimate it by averaging random samples.

Some concrete examples:

  • Physics: simulate the trajectories of neutrons in a nuclear reactor to estimate the fraction that escape without absorption.
  • Finance: simulate future price paths of an underlying asset to estimate the price of a complex option, especially path-dependent options.
  • Machine learning: use Monte Carlo to approximate Bayesian posterior distributions in models where the posterior has no closed form (Markov chain Monte Carlo, particle filters).
  • Statistics: bootstrap to estimate the variability of a sample statistic without needing a parametric model.
  • Computer graphics: path tracing algorithms estimate the integral of light over all paths from a scene to the camera.

Why This Matters

The Code Test estimation example is pedagogically simple, but the same principle scales to problems of enormous complexity. The only requirement is that you can simulate the random variable whose expectation you want. Once you can do that, the Law of Large Numbers does the rest, and the Central Limit Theorem tells you how uncertain your estimate is.

Problem Set

Problem 8.1. Run the example program with Code Test sample points. Record the standard error at each step and verify that it shrinks by roughly the factor of Code Test you would expect (since sample size quadruples each time and SE scales as Code Test).

Problem 8.2 (Reproducibility). Modify the example to accept an optional seed for the random state and use it to reproduce the same estimate multiple times. Why is reproducibility particularly important in scientific Monte Carlo work?

Problem 8.3 (Estimating an integral). Use Monte Carlo to estimate Code Test. Compare against the exact value, which is Code Test where Code Test is the standard normal CDF. What sample size do you need to get within Code Test of the true answer with 95% confidence?

Problem 8.4 (Buffon’s needle). Implement Buffon’s needle in Common Lisp. Drop a needle of length Code Test onto a floor with lines spaced Code Test apart. Estimate Code Test from Code Test drops and report the standard error. Compare the accuracy to the disk method with the same Code Test.

Problem 8.5 (Volume of a d-dimensional ball). Use Monte Carlo to estimate the volume of the unit ball in Code Test dimensions. The exact answer is Code Test, roughly Code Test. Now try Code Test and Code Test. As Code Test grows, most of the volume of the enclosing hypercube lies outside the ball, so the naive Monte Carlo estimator becomes very inefficient. Comment on your observations.

Problem 8.6 (Antithetic variates). Modify the Code Test estimator to use antithetic variates: for each random Code Test, also use Code Test. Report the estimate and standard error. Compare against the plain estimator with the same total sample size.

Problem 8.7 (Confidence interval). For Code Test sample points in the Code Test estimation, report a 95% confidence interval for Code Test using the CLT-based formula Code Test. Does the true Code Test lie inside your interval?

Problem 8.8 (High-dimensional integration). Use Monte Carlo to estimate the average value of the function Code Test over the unit hypercube Code Test. The exact answer is Code Test. Compare the Monte Carlo estimate against a direct grid-based numerical integration attempt.

Problem 8.9 (Coding exercise). Add a general function monte-carlo-mean that takes a thunk (a function of zero arguments returning a sample) and a sample size Code Test, and returns the estimated mean along with a 95% confidence interval using the CLT. Test it on the disk indicator for Code Test and on the exponential from previous chapters.