|
Packit |
67cb25 |
/* randist/gamma.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_sf_gamma.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_rng.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_randist.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static double gamma_large (const gsl_rng * r, const double a);
|
|
Packit |
67cb25 |
static double gamma_frac (const gsl_rng * r, const double a);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* The Gamma distribution of order a>0 is defined by:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
p(x) dx = {1 / \Gamma(a) b^a } x^{a-1} e^{-x/b} dx
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for x>0. If X and Y are independent gamma-distributed random
|
|
Packit |
67cb25 |
variables of order a1 and a2 with the same scale parameter b, then
|
|
Packit |
67cb25 |
X+Y has gamma distribution of order a1+a2.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The algorithms below are from Knuth, vol 2, 2nd ed, p. 129. */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double
|
|
Packit |
67cb25 |
gsl_ran_gamma_knuth (const gsl_rng * r, const double a, const double b)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* assume a > 0 */
|
|
Packit |
67cb25 |
unsigned int na = floor (a);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if(a >= UINT_MAX)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return b * (gamma_large (r, floor (a)) + gamma_frac (r, a - floor (a)));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (a == na)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return b * gsl_ran_gamma_int (r, na);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (na == 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return b * gamma_frac (r, a);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return b * (gsl_ran_gamma_int (r, na) + gamma_frac (r, a - na)) ;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double
|
|
Packit |
67cb25 |
gsl_ran_gamma_int (const gsl_rng * r, const unsigned int a)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (a < 12)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
unsigned int i;
|
|
Packit |
67cb25 |
double prod = 1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (i = 0; i < a; i++)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
prod *= gsl_rng_uniform_pos (r);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Note: for 12 iterations we are safe against underflow, since
|
|
Packit |
67cb25 |
the smallest positive random number is O(2^-32). This means
|
|
Packit |
67cb25 |
the smallest possible product is 2^(-12*32) = 10^-116 which
|
|
Packit |
67cb25 |
is within the range of double precision. */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return -log (prod);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return gamma_large (r, (double) a);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static double
|
|
Packit |
67cb25 |
gamma_large (const gsl_rng * r, const double a)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* Works only if a > 1, and is most efficient if a is large
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
This algorithm, reported in Knuth, is attributed to Ahrens. A
|
|
Packit |
67cb25 |
faster one, we are told, can be found in: J. H. Ahrens and
|
|
Packit |
67cb25 |
U. Dieter, Computing 12 (1974) 223-246. */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double sqa, x, y, v;
|
|
Packit |
67cb25 |
sqa = sqrt (2 * a - 1);
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
y = tan (M_PI * gsl_rng_uniform (r));
|
|
Packit |
67cb25 |
x = sqa * y + a - 1;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (x <= 0);
|
|
Packit |
67cb25 |
v = gsl_rng_uniform (r);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (v > (1 + y * y) * exp ((a - 1) * log (x / (a - 1)) - sqa * y));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return x;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static double
|
|
Packit |
67cb25 |
gamma_frac (const gsl_rng * r, const double a)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* This is exercise 16 from Knuth; see page 135, and the solution is
|
|
Packit |
67cb25 |
on page 551. */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double p, q, x, u, v;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (a == 0) {
|
|
Packit |
67cb25 |
return 0;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
p = M_E / (a + M_E);
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
u = gsl_rng_uniform (r);
|
|
Packit |
67cb25 |
v = gsl_rng_uniform_pos (r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (u < p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
x = exp ((1 / a) * log (v));
|
|
Packit |
67cb25 |
q = exp (-x);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
x = 1 - log (v);
|
|
Packit |
67cb25 |
q = exp ((a - 1) * log (x));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (gsl_rng_uniform (r) >= q);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return x;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double
|
|
Packit |
67cb25 |
gsl_ran_gamma_pdf (const double x, const double a, const double b)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (x < 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return 0 ;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (x == 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (a == 1)
|
|
Packit |
67cb25 |
return 1/b ;
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
return 0 ;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (a == 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return exp(-x/b)/b ;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double p;
|
|
Packit |
67cb25 |
double lngamma = gsl_sf_lngamma (a);
|
|
Packit |
67cb25 |
p = exp ((a - 1) * log (x/b) - x/b - lngamma)/b;
|
|
Packit |
67cb25 |
return p;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* New version based on Marsaglia and Tsang, "A Simple Method for
|
|
Packit |
67cb25 |
* generating gamma variables", ACM Transactions on Mathematical
|
|
Packit |
67cb25 |
* Software, Vol 26, No 3 (2000), p363-372.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Implemented by J.D.Lamb@btinternet.com, minor modifications for GSL
|
|
Packit |
67cb25 |
* by Brian Gough
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double
|
|
Packit |
67cb25 |
gsl_ran_gamma_mt (const gsl_rng * r, const double a, const double b)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return gsl_ran_gamma (r, a, b);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double
|
|
Packit |
67cb25 |
gsl_ran_gamma (const gsl_rng * r, const double a, const double b)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* assume a > 0 */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (a < 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double u = gsl_rng_uniform_pos (r);
|
|
Packit |
67cb25 |
return gsl_ran_gamma (r, 1.0 + a, b) * pow (u, 1.0 / a);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double x, v, u;
|
|
Packit |
67cb25 |
double d = a - 1.0 / 3.0;
|
|
Packit |
67cb25 |
double c = (1.0 / 3.0) / sqrt (d);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
while (1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
do
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
x = gsl_ran_gaussian_ziggurat (r, 1.0);
|
|
Packit |
67cb25 |
v = 1.0 + c * x;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
while (v <= 0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
v = v * v * v;
|
|
Packit |
67cb25 |
u = gsl_rng_uniform_pos (r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (u < 1 - 0.0331 * x * x * x * x)
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (log (u) < 0.5 * x * x + d * (1 - v + log (v)))
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return b * d * v;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|