Rejection Sampling

Rejection sampling is a technique used to generate random values from a target probability distribution. The key idea is to sample values from a proposal distribution that is easy to sample from, and reject values that don’t satisfy the desired distribution.

The algorithm works as follows:

  1. Sample a value x from the proposal distribution
  2. Sample a value u from the uniform distribution U(0,1)
  3. If u ≤ f(x)/M, accept x; otherwise reject x (f is the target PDF, M is a constant s.t. f(x)/M ≤ 1)
  4. Repeat until desired number of samples are accepted

This ensures the accepted values follow the target distribution f(x). The acceptance rate depends on the proposal distribution; a close match gives higher acceptance.

Example in Java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.util.Random;

class RejectionSampling {

  public static double sample(double min, double max) {
    Random rand = new Random();
    double x, u;
    do {
      x = rand.nextDouble() * (max - min) + min;
      u = rand.nextDouble();
    } while (u > Math.exp(-x*x/2)/0.39894); 
    return x;
  }

  public static void main(String[] args) {
    double x = sample(-4, 4);
    System.out.println(x);
  }

}

This samples from the standard normal distribution by using the uniform distribution as the proposal.

Examples in C++ and Python follow a similar structure. The key is using a proposal distribution, sampling values and accepting based on the target distribution.

Rejection Sampling is a technique in probability and statistics, in the realm of Monte Carlo methods, for generating samples from a complex distribution. If you want to generate samples from a target distribution but find it difficult to do so directly, you can sample from an easier distribution and then apply a rejection criterion to keep or discard each sample. Essentially, you “reject” samples that don’t fit the criteria of the distribution you are interested in.

Example Code

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.util.Random;

public class RejectionSampling {
    public static void main(String[] args) {
        Random random = new Random();
        int accepted = 0;
        int trials = 10000;

        for (int i = 0; i < trials; ++i) {
            double x = random.nextDouble();  // Sample x uniformly between 0 and 1
            double y = random.nextDouble();  // Sample y uniformly between 0 and 1

            if (y <= Math.pow(x, 2)) {  // Rejection criterion
                accepted++;
            }
        }
        double estimatedArea = (double) accepted / trials;
        System.out.println("Estimated area under x^2 curve: " + estimatedArea);
    }
}

C++

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(0.0, 1.0);

    int accepted = 0;
    int trials = 10000;

    for (int i = 0; i < trials; ++i) {
        double x = dis(gen);  // Sample x uniformly between 0 and 1
        double y = dis(gen);  // Sample y uniformly between 0 and 1

        if (y <= x * x) {  // Rejection criterion
            accepted++;
        }
    }
    double estimatedArea = static_cast<double>(accepted) / trials;
    std::cout << "Estimated area under x^2 curve: " << estimatedArea << std::endl;
    return 0;
}

Python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import random

accepted = 0
trials = 10000

for _ in range(trials):
    x = random.random()  # Sample x uniformly between 0 and 1
    y = random.random()  # Sample y uniformly between 0 and 1

    if y <= x ** 2:  # Rejection criterion
        accepted += 1

estimated_area = accepted / trials
print(f"Estimated area under x^2 curve: {estimated_area}")

The codes for all three languages follow the same logic: generate random points (x, y) and accept them based on whether they satisfy the criterion y <= x^2. The ratio of accepted points gives an estimation for the area under the curve y = x^2, in the range [0,1].

Very high frequency problems on rejection sampling:

  • Implement Rand10() Using Rand7()
  • Generate Random Point in a Circle