486 lines
13 KiB
D
486 lines
13 KiB
D
/**
|
|
Translation of SimpleRNG.
|
|
Removed the builtin RNG to use std.random, but kept the distribution functions.
|
|
John D. Cook confirmed this code as public domain.
|
|
|
|
Authors: John D. Cook.
|
|
See_also: $(WEB www.johndcook.com/cpp_random_number_generation.html)
|
|
*/
|
|
module gfm.math.simplerng;
|
|
|
|
public import std.random;
|
|
import std.math;
|
|
|
|
/// Returns: Normal (Gaussian) random sample.
|
|
/// See_also: Box-Muller algorithm.
|
|
double randNormal(RNG)(ref RNG rng, double mean = 0.0, double standardDeviation = 1.0)
|
|
{
|
|
assert(standardDeviation > 0);
|
|
double u1;
|
|
|
|
do
|
|
{
|
|
u1 = uniform01(rng);
|
|
} while (u1 == 0); // u1 must not be zero
|
|
double u2 = uniform01(rng);
|
|
double r = sqrt(-2.0 * log(u1));
|
|
double theta = 2.0 * double(PI) * u2;
|
|
return mean + standardDeviation * r * sin(theta);
|
|
}
|
|
|
|
/// Returns: Exponential random sample with specified mean.
|
|
double randExponential(RNG)(ref RNG rng, double mean = 1.0)
|
|
{
|
|
assert(mean > 0);
|
|
return -mean*log(uniform01(rng));
|
|
}
|
|
|
|
/// Returns: Gamma random sample.
|
|
/// See_also: "A Simple Method for Generating Gamma Variables"
|
|
/// by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
|
|
/// Vol 26, No 3, September 2000, pages 363-372.
|
|
double randGamma(RNG)(ref RNG rng, double shape, double scale)
|
|
{
|
|
double d, c, x, xsquared, v, u;
|
|
|
|
if (shape >= 1.0)
|
|
{
|
|
d = shape - 1.0/3.0;
|
|
c = 1.0/sqrt(9.0*d);
|
|
for (;;)
|
|
{
|
|
do
|
|
{
|
|
x = randNormal(rng);
|
|
v = 1.0 + c*x;
|
|
}
|
|
while (v <= 0.0);
|
|
v = v*v*v;
|
|
u = uniform01(rng);
|
|
xsquared = x*x;
|
|
if (u < 1.0 -.0331*xsquared*xsquared || log(u) < 0.5*xsquared + d*(1.0 - v + log(v)))
|
|
return scale*d*v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(shape > 0);
|
|
double g = randGamma(rng, shape+1.0, 1.0);
|
|
double w = uniform01(rng);
|
|
return scale*g*pow(w, 1.0/shape);
|
|
}
|
|
}
|
|
|
|
/// Returns: Chi-square sample.
|
|
double randChiSquare(RNG)(ref RNG rng, double degreesOfFreedom)
|
|
{
|
|
// A chi squared distribution with n degrees of freedom
|
|
// is a gamma distribution with shape n/2 and scale 2.
|
|
return randGamma(rng, 0.5 * degreesOfFreedom, 2.0);
|
|
}
|
|
|
|
/// Returns: Inverse-gamma sample.
|
|
double randInverseGamma(RNG)(ref RNG rng, double shape, double scale)
|
|
{
|
|
// If X is gamma(shape, scale) then
|
|
// 1/Y is inverse gamma(shape, 1/scale)
|
|
return 1.0 / randGamma(rng, shape, 1.0 / scale);
|
|
}
|
|
|
|
/// Returns: Weibull sample.
|
|
double randWeibull(RNG)(ref RNG rng, double shape, double scale)
|
|
{
|
|
assert(shape > 0 && scale > 0);
|
|
return scale * pow(-log(uniform01(rng)), 1.0 / shape);
|
|
}
|
|
|
|
/// Returns: Cauchy sample.
|
|
double randCauchy(RNG)(ref RNG rng, double median, double scale)
|
|
{
|
|
assert(scale > 0);
|
|
double p = uniform01(rng);
|
|
|
|
// Apply inverse of the Cauchy distribution function to a uniform
|
|
return median + scale*tan(double(PI)*(p - 0.5));
|
|
}
|
|
|
|
/// Returns: Student-t sample.
|
|
/// See_also: Seminumerical Algorithms by Knuth.
|
|
double randStudentT(RNG)(ref RNG rng, double degreesOfFreedom)
|
|
{
|
|
assert(degreesOfFreedom > 0);
|
|
|
|
|
|
double y1 = getNormal(rng);
|
|
double y2 = getChiSquare(rng, degreesOfFreedom);
|
|
return y1 / sqrt(y2 / degreesOfFreedom);
|
|
}
|
|
|
|
/// Returns: Laplace distribution random sample (also known as the double exponential distribution).
|
|
double randLaplace(RNG)(ref RNG rng, double mean, double scale)
|
|
{
|
|
double u = uniform01(rng);
|
|
return (u < 0.5) ? (mean + scale*log(2.0*u))
|
|
: (mean - scale*log(2*(1-u)));
|
|
}
|
|
|
|
/// Returns: Log-normal sample.
|
|
double randLogNormal(RNG)(ref RNG rng, double mu, double sigma)
|
|
{
|
|
return exp(getNormal(rng, mu, sigma));
|
|
}
|
|
|
|
/// Returns: Beta sample.
|
|
double randBeta(RNG)(ref RNG rng, double a, double b)
|
|
{
|
|
assert(a > 0 && b > 0);
|
|
|
|
// There are more efficient methods for generating beta samples.
|
|
// However such methods are a little more efficient and much more complicated.
|
|
// For an explanation of why the following method works, see
|
|
// http://www.johndcook.com/distribution_chart.html#gamma_beta
|
|
|
|
double u = getGamma(rng, a, 1.0);
|
|
double v = getGamma(rng, b, 1.0);
|
|
return u / (u + v);
|
|
}
|
|
|
|
/// Returns: Poisson sample.
|
|
int randPoisson(RNG)(ref RNG rng, double lambda)
|
|
{
|
|
return (lambda < 30.0) ? poissonSmall(rng, lambda) : poissonLarge(rng, lambda);
|
|
}
|
|
|
|
private
|
|
{
|
|
int poissonSmall(RNG)(ref RNG rng, double lambda)
|
|
{
|
|
// Algorithm due to Donald Knuth, 1969.
|
|
double p = 1.0, L = exp(-lambda);
|
|
int k = 0;
|
|
do
|
|
{
|
|
k++;
|
|
p *= uniform01(rng);
|
|
}
|
|
while (p > L);
|
|
return k - 1;
|
|
}
|
|
|
|
int poissonLarge(RNG)(ref RNG rng, double lambda)
|
|
{
|
|
// "Rejection method PA" from "The Computer Generation of Poisson Random Variables" by A. C. Atkinson
|
|
// Journal of the Royal Statistical Society Series C (Applied Statistics) Vol. 28, No. 1. (1979)
|
|
// The article is on pages 29-35. The algorithm given here is on page 32.
|
|
|
|
double c = 0.767 - 3.36/lambda;
|
|
double beta = double(PI)/sqrt(3.0*lambda);
|
|
double alpha = beta*lambda;
|
|
double k = log(c) - lambda - log(beta);
|
|
|
|
for(;;)
|
|
{
|
|
double u = uniform01(rng);
|
|
double x = (alpha - log((1.0 - u)/u))/beta;
|
|
int n = cast(int)(floor(x + 0.5));
|
|
if (n < 0)
|
|
continue;
|
|
double v = uniform01(rng);
|
|
double y = alpha - beta*x;
|
|
double temp = 1.0 + exp(y);
|
|
double lhs = y + log(v/(temp*temp));
|
|
double rhs = k + n*log(lambda) - logFactorial(n);
|
|
if (lhs <= rhs)
|
|
return n;
|
|
}
|
|
}
|
|
|
|
double logFactorial(int n) nothrow
|
|
{
|
|
assert(n >= 0);
|
|
if (n > 254)
|
|
{
|
|
double x = n + 1;
|
|
return (x - 0.5)*log(x) - x + 0.5*log(2*double(PI)) + 1.0/(12.0*x);
|
|
}
|
|
else
|
|
{
|
|
return LOG_FACTORIAL[n];
|
|
}
|
|
}
|
|
}
|
|
|
|
private static immutable double[255] LOG_FACTORIAL =
|
|
[
|
|
0.000000000000000,
|
|
0.000000000000000,
|
|
0.693147180559945,
|
|
1.791759469228055,
|
|
3.178053830347946,
|
|
4.787491742782046,
|
|
6.579251212010101,
|
|
8.525161361065415,
|
|
10.604602902745251,
|
|
12.801827480081469,
|
|
15.104412573075516,
|
|
17.502307845873887,
|
|
19.987214495661885,
|
|
22.552163853123421,
|
|
25.191221182738683,
|
|
27.899271383840894,
|
|
30.671860106080675,
|
|
33.505073450136891,
|
|
36.395445208033053,
|
|
39.339884187199495,
|
|
42.335616460753485,
|
|
45.380138898476908,
|
|
48.471181351835227,
|
|
51.606675567764377,
|
|
54.784729398112319,
|
|
58.003605222980518,
|
|
61.261701761002001,
|
|
64.557538627006323,
|
|
67.889743137181526,
|
|
71.257038967168000,
|
|
74.658236348830158,
|
|
78.092223553315307,
|
|
81.557959456115029,
|
|
85.054467017581516,
|
|
88.580827542197682,
|
|
92.136175603687079,
|
|
95.719694542143202,
|
|
99.330612454787428,
|
|
102.968198614513810,
|
|
106.631760260643450,
|
|
110.320639714757390,
|
|
114.034211781461690,
|
|
117.771881399745060,
|
|
121.533081515438640,
|
|
125.317271149356880,
|
|
129.123933639127240,
|
|
132.952575035616290,
|
|
136.802722637326350,
|
|
140.673923648234250,
|
|
144.565743946344900,
|
|
148.477766951773020,
|
|
152.409592584497350,
|
|
156.360836303078800,
|
|
160.331128216630930,
|
|
164.320112263195170,
|
|
168.327445448427650,
|
|
172.352797139162820,
|
|
176.395848406997370,
|
|
180.456291417543780,
|
|
184.533828861449510,
|
|
188.628173423671600,
|
|
192.739047287844900,
|
|
196.866181672889980,
|
|
201.009316399281570,
|
|
205.168199482641200,
|
|
209.342586752536820,
|
|
213.532241494563270,
|
|
217.736934113954250,
|
|
221.956441819130360,
|
|
226.190548323727570,
|
|
230.439043565776930,
|
|
234.701723442818260,
|
|
238.978389561834350,
|
|
243.268849002982730,
|
|
247.572914096186910,
|
|
251.890402209723190,
|
|
256.221135550009480,
|
|
260.564940971863220,
|
|
264.921649798552780,
|
|
269.291097651019810,
|
|
273.673124285693690,
|
|
278.067573440366120,
|
|
282.474292687630400,
|
|
286.893133295426990,
|
|
291.323950094270290,
|
|
295.766601350760600,
|
|
300.220948647014100,
|
|
304.686856765668720,
|
|
309.164193580146900,
|
|
313.652829949878990,
|
|
318.152639620209300,
|
|
322.663499126726210,
|
|
327.185287703775200,
|
|
331.717887196928470,
|
|
336.261181979198450,
|
|
340.815058870798960,
|
|
345.379407062266860,
|
|
349.954118040770250,
|
|
354.539085519440790,
|
|
359.134205369575340,
|
|
363.739375555563470,
|
|
368.354496072404690,
|
|
372.979468885689020,
|
|
377.614197873918670,
|
|
382.258588773060010,
|
|
386.912549123217560,
|
|
391.575988217329610,
|
|
396.248817051791490,
|
|
400.930948278915760,
|
|
405.622296161144900,
|
|
410.322776526937280,
|
|
415.032306728249580,
|
|
419.750805599544780,
|
|
424.478193418257090,
|
|
429.214391866651570,
|
|
433.959323995014870,
|
|
438.712914186121170,
|
|
443.475088120918940,
|
|
448.245772745384610,
|
|
453.024896238496130,
|
|
457.812387981278110,
|
|
462.608178526874890,
|
|
467.412199571608080,
|
|
472.224383926980520,
|
|
477.044665492585580,
|
|
481.872979229887900,
|
|
486.709261136839360,
|
|
491.553448223298010,
|
|
496.405478487217580,
|
|
501.265290891579240,
|
|
506.132825342034830,
|
|
511.008022665236070,
|
|
515.890824587822520,
|
|
520.781173716044240,
|
|
525.679013515995050,
|
|
530.584288294433580,
|
|
535.496943180169520,
|
|
540.416924105997740,
|
|
545.344177791154950,
|
|
550.278651724285620,
|
|
555.220294146894960,
|
|
560.169054037273100,
|
|
565.124881094874350,
|
|
570.087725725134190,
|
|
575.057539024710200,
|
|
580.034272767130800,
|
|
585.017879388839220,
|
|
590.008311975617860,
|
|
595.005524249382010,
|
|
600.009470555327430,
|
|
605.020105849423770,
|
|
610.037385686238740,
|
|
615.061266207084940,
|
|
620.091704128477430,
|
|
625.128656730891070,
|
|
630.172081847810200,
|
|
635.221937855059760,
|
|
640.278183660408100,
|
|
645.340778693435030,
|
|
650.409682895655240,
|
|
655.484856710889060,
|
|
660.566261075873510,
|
|
665.653857411105950,
|
|
670.747607611912710,
|
|
675.847474039736880,
|
|
680.953419513637530,
|
|
686.065407301994010,
|
|
691.183401114410800,
|
|
696.307365093814040,
|
|
701.437263808737160,
|
|
706.573062245787470,
|
|
711.714725802289990,
|
|
716.862220279103440,
|
|
722.015511873601330,
|
|
727.174567172815840,
|
|
732.339353146739310,
|
|
737.509837141777440,
|
|
742.685986874351220,
|
|
747.867770424643370,
|
|
753.055156230484160,
|
|
758.248113081374300,
|
|
763.446610112640200,
|
|
768.650616799717000,
|
|
773.860102952558460,
|
|
779.075038710167410,
|
|
784.295394535245690,
|
|
789.521141208958970,
|
|
794.752249825813460,
|
|
799.988691788643450,
|
|
805.230438803703120,
|
|
810.477462875863580,
|
|
815.729736303910160,
|
|
820.987231675937890,
|
|
826.249921864842800,
|
|
831.517780023906310,
|
|
836.790779582469900,
|
|
842.068894241700490,
|
|
847.352097970438420,
|
|
852.640365001133090,
|
|
857.933669825857460,
|
|
863.231987192405430,
|
|
868.535292100464630,
|
|
873.843559797865740,
|
|
879.156765776907600,
|
|
884.474885770751830,
|
|
889.797895749890240,
|
|
895.125771918679900,
|
|
900.458490711945270,
|
|
905.796028791646340,
|
|
911.138363043611210,
|
|
916.485470574328820,
|
|
921.837328707804890,
|
|
927.193914982476710,
|
|
932.555207148186240,
|
|
937.921183163208070,
|
|
943.291821191335660,
|
|
948.667099599019820,
|
|
954.046996952560450,
|
|
959.431492015349480,
|
|
964.820563745165940,
|
|
970.214191291518320,
|
|
975.612353993036210,
|
|
981.015031374908400,
|
|
986.422203146368590,
|
|
991.833849198223450,
|
|
997.249949600427840,
|
|
1002.670484599700300,
|
|
1008.095434617181700,
|
|
1013.524780246136200,
|
|
1018.958502249690200,
|
|
1024.396581558613400,
|
|
1029.838999269135500,
|
|
1035.285736640801600,
|
|
1040.736775094367400,
|
|
1046.192096209724900,
|
|
1051.651681723869200,
|
|
1057.115513528895000,
|
|
1062.583573670030100,
|
|
1068.055844343701400,
|
|
1073.532307895632800,
|
|
1079.012946818975000,
|
|
1084.497743752465600,
|
|
1089.986681478622400,
|
|
1095.479742921962700,
|
|
1100.976911147256000,
|
|
1106.478169357800900,
|
|
1111.983500893733000,
|
|
1117.492889230361000,
|
|
1123.006317976526100,
|
|
1128.523770872990800,
|
|
1134.045231790853000,
|
|
1139.570684729984800,
|
|
1145.100113817496100,
|
|
1150.633503306223700,
|
|
1156.170837573242400,
|
|
];
|
|
|
|
unittest
|
|
{
|
|
Xorshift32 rng;
|
|
rng.seed(unpredictableSeed());
|
|
|
|
double x = randNormal!Xorshift32(rng, 0.0, 1.0);
|
|
x = randExponential!Xorshift32(rng);
|
|
x = randGamma!Xorshift32(rng, 1.2, 1.0);
|
|
x = randGamma!Xorshift32(rng, 0.8, 2.0);
|
|
x = randChiSquare!Xorshift32(rng, 2.0);
|
|
x = randInverseGamma!Xorshift32(rng, 1.1, 0.7);
|
|
x = randWeibull!Xorshift32(rng, 3.0, 0.7);
|
|
x = randCauchy!Xorshift32(rng, 5.0, 1.4);
|
|
}
|