/** 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); }