Handout 9

Generating Random Values from Non-Uniform Distributions*


Direct Methods

Direct methods make direct use of the definition of the distribution.

For example, consider binomial random numbers. You can think of a binomial random number as the number of heads in N tosses of a coin with probability p of a heads on any toss. If you generate N uniform random numbers on the interval (0,1) and count the number less than p, then the count is a binomial random number with parameters N and p.

The following function is a simple implementation of a binomial RNG using this approach:

function X = directbinornd(N,p,m,n)

X = zeros(m,n); % Preallocate memory
for i = 1:m*n
    u = rand(N,1);
    X(i) = sum(u < p);
end

For example,

X = directbinornd(100,0.3,1e4,1);
hist(X,101)

Direct Methods (another binomial example)

You can think of binomial random numbers as the number of heads in n tosses of a coin with probability p of a heads on any toss. If you generate n uniform random numbers and count the number that are greater than p, the result is binomial with parameters n and p. 

determinations = 10000;
n = 100;
p = 1 - 0.3;
result = zeros(determinations, 1);

for i = 1:determinations
  count = 0;
  for j = 1:n
    if(rand > p) 
      count = count + 1;
    end 
  end
result(i,1) = count;
end

hist(result, 101);

Continuous Distribution Inversion Methods

Inversion methods are based on the observation that continuous cumulative distribution functions (cdfs) range uniformly over the interval (0,1). If u is a uniform random number on (0,1), then a random number X from a continuous distribution with specified cdf F can be obtained using X = F-1(U).

Exponential CDF with parameter lambda = F(x, lambda) =  p = 1 - exp(-lambda * x) 

Inverse exponential CDF with parameter lambda =  F-1(p, lambda) = x = -ln(1 - p)/lambda

For example, the following code generates random numbers from a specific exponential distribution using the inverse cdf and the MATLAB uniform random number generator rand:

determinations = 10000;
lambda = 1;
result = zeros(determinations, 1);

for i = 1:determinations
    result(i,1) = -log(1 - rand) / lambda;
end

numbins = 101;
hist(result,numbins)
hold on
[bincounts,binpositions] = hist(result,numbins);
binwidth = binpositions(2) - binpositions(1);
histarea = binwidth*sum(bincounts);
x = binpositions(1):0.001:binpositions(end);
y = (1 / lambda) * exp(-x / lambda);
plot(x,histarea*y,'r','LineWidth',2)

Continuous Distribution Inversion Methods

Inversion methods can be adapted to discrete distributions. Suppose you want a random number X from a discrete distribution with a probability mass vector P(X = xi) = pi, where x0 < x1 < x2 < ... . You could generate a uniform random number u on (0,1) and then set X = xi if F(xi�1) < u < F(xi).

For example, the following function implements an inversion method for a discrete distribution with probability mass vector p:

determinations = 10000;
p = [0.1 0.2 0.3 0.2 0.1 0.1]; % Probability mass vector;
sum_p = zeros(length(p),1);
sum_p(1) = p(1);

for i = 2:length(p)
  sum_p(i) = p(i) + sum_p(i - 1);
end

result = zeros(determinations, 1);

for i = 1:determinations
  x = rand;
  for j = 1:length(p)
    if x < sum_p(j);
      result(i,1) = j;
      break;
    end 
  end 
end

hist(result, length(p));

Acceptance-Rejection Methods

The functional form of some distributions makes it difficult or time-consuming to generate random numbers using direct or inversion methods. Acceptance-rejection methods can provide a good solution in these cases.

Acceptance-rejection methods also begin with uniform random numbers, but they require the availability of an additional random number generator. If the goal is to generate a random number from a continuous distribution with pdf f, acceptance-rejection methods first generate a random number from a continuous distribution with pdf g satisfying f (x) ≤ cg (x) for some c and all x. 

A continuous acceptance-rejection RNG proceeds as follows:


1. Choose a density g.
2. Find a constant c such that f (x) / g (x) ≤ c for all x.
3. Generate a uniform random number u.
4. Generate a random number v from g.
5. If c*u ≤ f (v) / g (v) , accept and return v.
6. Otherwise, reject v and go to 3.

For efficiency, you need a cheap method for generating random numbers from g, and the scalar c should be small. The expected number of iterations to produce a random number is c.

For example, the function f (x) = xe�x^2/2 satisfies the conditions for a pdf on [0,∞) (nonnegative and integrates to 1). The exponential pdf with mean 1, f (x) = e�x, dominates g for c greater than about 2.2. Thus, you can use rand to generate random numbers from f :

determinations = 10000;
result = zeros(determinations, 1);
c = 2.2;

% f = x.*exp(-(x.^2)/2);
% g = exp(-x);

X = zeros(determinations, 1); % Preallocate memory
for i = 1:determinations
  accept = false;
  while accept == false
    u = rand();
    v = -log(1 - rand) / lambda; % exponential rng using the inversion method
    if c*u <= v.*exp(-(v.^2)/2) / exp(-v)
      result(i) = v;
      accept = true;
    end
  end
end

hist(result, 101);


* portions from here.