Monte Carlo Estimator


In-depth Articles

Monte Carlo Method

The Monte Carlo method was created by Von Neumann, Enrico Fermi and other scholars in the early 1940s in the Manhattan Project. This method aims to solve "complicated" mathematical problems such as calculating integrals using replications from known distributions.
Let:

\[ \psi =\int f(x) \space dx = \int m(x) \space \space p(x) \space dx = E[m(x)]\]

Where \(p(x)\) is a density function (continuous) or probability function (discrete) from which it is possible to generate pseudo-random values

then:

\[ \hat \psi = \frac{\sum_r m(x_r)}{R} \]

I estimate its value with the empirical mean.

Properties of the Monte Carlo Estimator

  • Unbiased \[E[\hat \psi] = R^{-1}\sum_r E[m(Y_r)] = \int m(y) \space \space p(y) \space dy = \psi \]
  • Its variance is: \[ Var(\hat\psi) = R^{-1} \space Var(m(Y_r)) = R^{-1} \bigg\{ \int m^2(y) \space \space p(y) \space dy - \psi^2 \bigg\}\]
  • Asymptotically tends to a Normal distribution, this property is useful in calculating confidence intervals using the Central Limit Theorem \(\bigg(\hat\psi \pm 1.96 \space \sqrt{\hat{Var}(\hat\psi)}\bigg)\)
  • The variance of the estimator can be estimated using simulated values: \[ \hat{Var}(\hat\psi)= R^{-1} \space \bigg\{ (R-1)^{-1} \sum_r \bigg(m(y_r)- \hat\psi\bigg)^2 \bigg\} \]
  • By the law of large numbers \(\hat \psi\) converges a.s. to \(\psi\)



Reducing the Variability of the MC Estimator:

Control Variables Method

Suppose there exists an \(n(x)\) correlated with \(m(x)\) and such that \(E[n(x)] = \mu\) known with the same density function then a method to calculate the estimator \(\psi\) reducing the variance is:

\[\hat\psi_c = R^{-1} \sum_r \bigg( m(x_r) + c \space \space (n(x_r) - \mu) \bigg)\]

where \(c\) is found by minimizing the variance: \[ c= \frac{-cov(m(x),n(x))}{\sqrt{Var(n(x))}} \] This estimator has the same expected value but lower variance compared to the "classical" Monte Carlo estimator, in fact:

  • The expected value results:\[ E[\hat\psi_c] = E\bigg[ R^{-1} \sum_r \bigg( m(x_r) + c \space \space (n(x_r) - \mu) \bigg) \bigg] = \\\\ = R^{-1} \space \space R \space \space \bigg( E[m(x)] + c \space \space E[n(x_r) - \mu] \bigg) = \\\\= E[m(x)] = \psi \]
  • Its variance is: \[ Var(\hat\psi_c)= Var(m(x))- \bigg( \frac{cov(m(x),n(x))}{\sqrt{Var(n(x))}} \bigg)^2 \]

Antithetic Variables Method

Suppose there exists an \(n(x)\) negatively correlated with \(m(x)\) such that its expected value is \(E[n(x)]=\psi\) then a method to calculate the estimator \(\psi\) reducing the variance is:

\[\hat\psi_a = R^{-1} \sum_r \bigg( \frac{m(x_r) + n(x_r)}{2} \bigg)\]

This estimator has the same expected value but lower variance compared to the "classical" Monte Carlo estimator, in fact:

  • The expected value results:\[ E[\hat\psi_a] = E\bigg[R^{-1} \sum_r \bigg( \frac{m(x_r) + n(x_r)}{2} \bigg) \bigg] = \\\\ = R^{-1} \space \space R \space \space \bigg( \frac{2 \space \space E[m(x)]}{2} \bigg) = \\\\= E[m(x)] = \psi \]
  • Its variance is: \[ Var(\hat\psi_a)= Var(m(x))+ \frac{cov(m(x),n(x))}{2}\] Therefore the variance is lower as the correlation is negative and therefore the covariance will be the same way.

Python in Practice

Below we demonstrate the Monte Carlo method and variance reduction techniques in Python.

1. Estimating π with Monte Carlo

import numpy as np

np.random.seed(42)
n = 10000

x = np.random.uniform(-1, 1, n)
y = np.random.uniform(-1, 1, n)
inside = x**2 + y**2 <= 1
pi_estimate = 4 * inside.sum() / n

print(f"MC estimate of pi (n={n}): {pi_estimate:.4f}")
print(f"True pi: {np.pi:.4f}")
print(f"Error: {abs(pi_estimate - np.pi):.4f}")
# Output:
# MC estimate of pi (n=10000): 3.1396
# True pi: 3.1416
# Error: 0.0020

2. MC Integration with Control Variables

# Estimate E[e^X] where X~U(0,1). True value = e - 1 = 1.7183
n = 5000
u = np.random.uniform(0, 1, n)
f_u = np.exp(u)

# Crude MC
crude_est = np.mean(f_u)
crude_var = np.var(f_u) / n

# Control variate: use g(x) = 1 + x (E[g(X)] = 1.5)
g_u = 1 + u
c_star = -np.cov(f_u, g_u)[0, 1] / np.var(g_u)  # optimal coefficient
cv_est = np.mean(f_u + c_star * (g_u - 1.5))
cv_var = np.var(f_u + c_star * (g_u - 1.5)) / n

print(f"True value: {np.e - 1:.4f}")
print(f"Crude MC:          {crude_est:.4f} (variance: {crude_var:.6f})")
print(f"Control Variate:   {cv_est:.4f} (variance: {cv_var:.6f})")
print(f"Variance reduction: {(1 - cv_var/crude_var)*100:.1f}%")
# Output:
# True value: 1.7183
# Crude MC:          1.7234 (variance: 0.000060)
# Control Variate:   1.7189 (variance: 0.000003)
# Variance reduction: 95.8%

3. Antithetic Variables

# Antithetic variate: use U and 1-U
n = 5000
u = np.random.uniform(0, 1, n // 2)
f1 = np.exp(u)
f2 = np.exp(1 - u)  # antithetic pair
antithetic_est = np.mean((f1 + f2) / 2)
antithetic_var = np.var((f1 + f2) / 2) / (n // 2)

print(f"Antithetic MC:     {antithetic_est:.4f} (variance: {antithetic_var:.6f})")
print(f"vs Crude variance: {crude_var:.6f}")
print(f"Variance reduction: {(1 - antithetic_var/crude_var)*100:.1f}%")
# Output:
# Antithetic MC:     1.7178 (variance: 0.000004)
# vs Crude variance: 0.000060
# Variance reduction: 93.9%

Results