blob: 75c4d3e99020c4fcd3c26319ad0cd86ac7a06b9f [file]
/* Copyright 2011 Google Inc. All Rights Reserved.
* Author: asr@google.com (Abhishek Srivastava)
*/
#include "linsched_rand.h"
#include <math.h>
#include <stdlib.h>
/* seed the linsched_rand()
* XORing with MASK to allow seed to be 0 */
int *linsched_init_rand(const unsigned int seed)
{
unsigned int *state = malloc(sizeof(unsigned int));
*state = seed ^ MASK;
return state;
}
/* destroy() required after init() */
void linsched_destroy_rand(unsigned int *state)
{
if (state)
free(state);
}
/* * Lehmer random number generator (ref. Knuth (AoCP) )
* * call linsched_srand() before calling linsched_rand()
* * n = 2147483647 (Mersenne Prime)
* * g = 16807 */
double linsched_rand(unsigned int *const state)
{
unsigned int rand = (16807 * (*state)) % 2147483647;
*state = rand;
return rand / (double) 2147483647;
}
/* return a random number from [low, high)
* low and high must be positive
* rand_state must be initialized by linsched_init_rand() */
double linsched_rand_range(double low, double high,
unsigned int *rand_state)
{
double rval = linsched_rand(rand_state);
return low + rval * (high - low);
}
/* generates a gaussian deviate with given parameters
* (ref GSL's gsl_ran_gaussian) */
double linsched_gen_gaussian_dist(struct rand_dist *rdist)
{
int mu = ((struct gaussian_dist *) rdist->dist)->mu;
int sigma = ((struct gaussian_dist *) rdist->dist)->sigma;
unsigned int *rand_state =
((struct gaussian_dist *) rdist->dist)->rand_state;
double x, y, r2;
do {
x = -1 + 2 * linsched_rand(rand_state);
y = -1 + 2 * linsched_rand(rand_state);
r2 = x * x + y * y;
} while (r2 > 1.0 || r2 == 0);
return mu + sigma * y * sqrt(-2.0 * log(r2) / r2);
}
/* takes the log of gamma(xx)
* (ref. Numerical Recipies in C : Pg 214 */
double gammaln(const double xx)
{
double x, y, tmp, ser;
static double cof[6] =
{ 76.18009172947146, -86.50532032941677, 24.01409824083091,
-1.231739572450155, 0.1208650973866170e-2,
-0.5395239384953e-5
};
int j;
y = x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * log(tmp);
ser = 1.000000000190015;
for (j = 0; j <= 5; j++)
ser += cof[j] / ++y;
return -tmp + log(2.5066282746310005 * ser / x);
}
/* generates a poisson distribution given mean mu
* (ref. Numerical Recipies in C : Pg 294 */
double linsched_gen_poisson_dist(struct rand_dist *rdist)
{
int mu = ((struct poisson_dist *) rdist->dist)->mu;
unsigned int *rand_state =
((struct poisson_dist *) rdist->dist)->rand_state;
double sq = 0.0, alxm = 0.0, g = 0.0, oldm = (-1.0);
double em, t, y;
if (mu < 12.0) {
if (mu != oldm) {
oldm = mu;
g = exp(-mu);
}
em = -1;
t = 1.0;
do {
++em;
t *= linsched_rand(rand_state);
} while (t > g);
} else {
if (mu != oldm) {
oldm = mu;
sq = sqrt(2.0 * mu);
alxm = log(mu);
g = mu * alxm - gammaln(mu + 1.0);
}
do {
do {
y = tan(M_PI * linsched_rand(rand_state));
em = sq * y + mu;
} while (em < 0.0);
em = floor(em);
t = 0.9 * (1.0 + y * y) * exp(em * alxm -
gammaln(em + 1.0)
- g);
} while (linsched_rand(rand_state) > t);
}
return em;
}
/* generates an exponential distribution given mean mu
* (ref. Numerical Recipies in C : Pg 287 */
double linsched_gen_exp_dist(struct rand_dist *rdist)
{
double edev;
double mu = ((struct exp_dist *) rdist->dist)->mu;
unsigned int *rand_state =
((struct exp_dist *) rdist->dist)->rand_state;
do {
edev = linsched_rand(rand_state);
} while (edev == 0.0);
return -mu * log(edev);
}
/* generates lognormal deviates using a std gaussian N(0,1)
* ref. wikipedia (http://en.wikipedia.org/wiki/Log-normal_distribution) */
double linsched_gen_lognormal_dist(struct rand_dist *rdist)
{
double lndev, gaussdev;
struct lognormal_dist *ldist = ((struct lognormal_dist *) rdist->dist);
double meanlog = ldist->meanlog;
double sdlog = ldist->sdlog;
struct rand_dist *gdist = ((struct rand_dist *) ldist->std_gauss_dist);
gaussdev = linsched_gen_gaussian_dist(gdist);
lndev = exp(meanlog + gaussdev * sdlog);
return lndev;
}
struct rand_dist *linsched_init_gaussian(const int mu, const int sigma,
const int seed)
{
struct rand_dist *rdist = malloc(sizeof(struct rand_dist));
struct gaussian_dist *gdist = malloc(sizeof(struct gaussian_dist));
gdist->mu = mu;
gdist->sigma = sigma;
gdist->rand_state = linsched_init_rand(seed);
rdist->type = GAUSSIAN;
rdist->gen_fn = linsched_gen_gaussian_dist;
rdist->dist = gdist;
return rdist;
}
void linsched_destroy_gaussian(struct rand_dist *rdist)
{
unsigned int *rand_state =
((struct gaussian_dist *) rdist->dist)->rand_state;
if (rdist) {
if (rdist->dist) {
linsched_destroy_rand(rand_state);
free(rdist->dist);
}
free(rdist);
}
}
struct rand_dist *linsched_init_poisson(const int mu, const int seed)
{
struct rand_dist *rdist = malloc(sizeof(struct rand_dist));
struct poisson_dist *pdist = malloc(sizeof(struct poisson_dist));
pdist->mu = mu;
pdist->rand_state = linsched_init_rand(seed);
rdist->type = POISSON;
rdist->gen_fn = linsched_gen_poisson_dist;
rdist->dist = pdist;
return rdist;
}
void linsched_destroy_poisson(struct rand_dist *rdist)
{
unsigned int *rand_state =
((struct poisson_dist *) rdist->dist)->rand_state;
if (rdist) {
if (rdist->dist) {
linsched_destroy_rand(rand_state);
free(rdist->dist);
}
free(rdist);
}
}
struct rand_dist *linsched_init_exponential(const double mu, const int seed)
{
struct rand_dist *rdist = malloc(sizeof(struct rand_dist));
struct exp_dist *edist = malloc(sizeof(struct exp_dist));
edist->mu = mu;
edist->rand_state = linsched_init_rand(seed);
rdist->type = EXPONENTIAL;
rdist->gen_fn = linsched_gen_exp_dist;
rdist->dist = edist;
return rdist;
}
void linsched_destroy_exponential(struct rand_dist *rdist)
{
unsigned int *rand_state;
if (rdist) {
if (rdist->dist) {
rand_state =
((struct exp_dist *) rdist->dist)->rand_state;
linsched_destroy_rand(rand_state);
free(rdist->dist);
}
free(rdist);
}
}
struct rand_dist *linsched_init_lognormal(const double meanlog,
const double sdlog, const int seed)
{
struct rand_dist *rdist = malloc(sizeof(struct rand_dist));
struct lognormal_dist *ldist = malloc(sizeof(struct lognormal_dist));
ldist->meanlog = meanlog;
ldist->sdlog = sdlog;
ldist->std_gauss_dist = linsched_init_gaussian(0, 1, seed);
rdist->type = LOGNORMAL;
rdist->gen_fn = linsched_gen_lognormal_dist;
rdist->dist = ldist;
return rdist;
}
void linsched_destroy_lognormal(struct rand_dist *rdist)
{
struct lognormal_dist *ldist = ((struct lognormal_dist *) rdist->dist);
struct rand_dist *gdist = ((struct rand_dist *) ldist->std_gauss_dist);
if (rdist) {
if (rdist->dist) {
linsched_destroy_gaussian(gdist);
free(ldist);
}
free(rdist);
}
}
void linsched_destroy_dist(struct rand_dist *rdist)
{
switch (rdist->type) {
case GAUSSIAN:
linsched_destroy_gaussian(rdist);
break;
case POISSON:
linsched_destroy_poisson(rdist);
break;
case EXPONENTIAL:
linsched_destroy_exponential(rdist);
break;
case LOGNORMAL:
linsched_destroy_lognormal(rdist);
break;
}
}
struct rand_dist *linsched_copy_dist(const struct rand_dist *rdist,
unsigned int *rand_state)
{
linsched_rand(rand_state);
switch (rdist->type) {
case GAUSSIAN: {
struct gaussian_dist *dist = rdist->dist;
return linsched_init_gaussian(dist->mu, dist->sigma,
*rand_state);
}
case POISSON: {
struct poisson_dist *dist = rdist->dist;
return linsched_init_poisson(dist->mu, *rand_state);
}
case EXPONENTIAL: {
struct exp_dist *dist = rdist->dist;
return linsched_init_exponential(dist->mu, *rand_state);
}
case LOGNORMAL: {
struct lognormal_dist *dist = rdist->dist;
return linsched_init_lognormal(dist->meanlog, dist->sdlog,
*rand_state);
}
default:
return NULL;
}
}