vide_public/external/libsdf/libsw/randoms.c
2013-03-01 15:43:05 -05:00

126 lines
3 KiB
C

#include "error.h"
#include "randoms.h"
/* ran2 from Numerical Recipes 2nd ed. modified to conform to our interface */
/* You win $1000 from Press et al. if it fails non-trivially */
#define IM1 2147483563
#define IM2 2147483399
#define AM (1.0/IM1)
#define IMM1 (IM1-1)
#define IA1 40014
#define IA2 40692
#define IQ1 53668
#define IQ2 52774
#define IR1 12211
#define IR2 3791
#define NDIV (1+IMM1/NTAB)
#define EPS 1.2e-7
#define RNMX (1.0-EPS)
void
ran_init(int seed, ran_state *s)
{
int j;
long k;
if (seed < 1)
Error("Bad seed in ran_init2 (%d)\n", seed);
s->idum = s->idum2 = seed;
s->next_norml_ok = 0;
for (j=NTAB+7;j>=0;j--) {
k=(s->idum)/IQ1;
s->idum=IA1*(s->idum-k*IQ1)-k*IR1;
if (s->idum < 0) s->idum += IM1;
if (j < NTAB) s->iv[j] = s->idum;
}
s->iy=s->iv[0];
s->did_init = IQ1;
}
float
uniform_rand(ran_state *s)
{
int j;
long k;
float temp;
if (s->did_init != IQ1)
Error("You forgot to call ran_init2\n");
k=(s->idum)/IQ1;
s->idum=IA1*(s->idum-k*IQ1)-k*IR1;
if (s->idum < 0) s->idum += IM1;
k=s->idum2/IQ2;
s->idum2=IA2*(s->idum2-k*IQ2)-k*IR2;
if (s->idum2 < 0) s->idum2 += IM2;
j=s->iy/NDIV;
s->iy=s->iv[j]-s->idum2;
s->iv[j] = s->idum;
if (s->iy < 1) s->iy += IMM1;
if ((temp=AM*s->iy) > RNMX) return RNMX;
else return temp;
}
float normal_rand(ran_state *st)
/*
This is the Polar method for normal distributions, as described on or near
page 104 of Knuth, Semi-numerical Algorithms. To quote Knuth, "The polar
method is quite slow, but it has essentially perfect accuracy, and it is very
easy to write a program for the polar method..." 'nuf said. Algorithm due
to Box, Muller and Marsaglia.
*/
{
float v1, v2; /* uniformly distributed on [-1, 1) */
float s; /* radius of a point pulled from a uniform circle */
float foo; /* A useful intermediate value. */
double log(double), sqrt(double);
if(st->next_norml_ok){
st->next_norml_ok = 0;
return st->next_norml;
}
do{
v1 = 2.0F * uniform_rand(st) - 1.0F;
v2 = 2.0F * uniform_rand(st) - 1.0F;
s = v1*v1 + v2*v2;
} while(s >= 1.0F);
foo = sqrt( -2.0F * log(s)/s);
st->next_norml_ok = 1;
st->next_norml = v1*foo;
return v2*foo;
}
/* Return a uniform point in a ndim-sphere by rejection. */
/* If ndim is large (bigger than 4 or so), this becomes very inefficient */
/* Return the radius-squard of the result. */
float sphere_rand(ran_state *st, int ndim, float *x)
{
int k;
float rsqx;
do {
rsqx = (float)0.0;
for (k = 0; k < ndim; k++) {
x[k] = uniform_rand(st)*2.0F - 1.0F; /* a pt in (-1,1) */
rsqx += x[k] * x[k];
}
} while (rsqx > (float)1.0);
return rsqx;
}
/* Return a uniform point in a ndim-cube */
/* Return the radius-squard of the result. */
float cube_rand(ran_state *st, int ndim, float *x)
{
int k;
float rsqx;
rsqx = (float)0.0;
for (k = 0; k < ndim; k++) {
x[k] = uniform_rand(st)*2.0F - 1.0F; /* a pt in (-1,1) */
rsqx += x[k] * x[k];
}
return rsqx;
}