| 1 |
/**Generates random samples from a various probability distributions. |
|---|
| 2 |
* These are mostly D ports of the NumPy random number generators.*/ |
|---|
| 3 |
|
|---|
| 4 |
/* This library is a D port of a large portion of the Numpy random number |
|---|
| 5 |
* library. A few distributions were excluded because they were too obscure |
|---|
| 6 |
* to be tested properly. They may be included at some point in the future. |
|---|
| 7 |
* |
|---|
| 8 |
* Port to D copyright 2009 David Simcha. |
|---|
| 9 |
* |
|---|
| 10 |
* The original C code is available under the licenses below. No additional |
|---|
| 11 |
* restrictions shall apply to this D translation. Eventually, I will try to |
|---|
| 12 |
* discuss the licensing issues with the original authors of Numpy and |
|---|
| 13 |
* make this sane enough that this module can be included in Phobos without |
|---|
| 14 |
* concern. For now, it's free enough that you can at least use it in |
|---|
| 15 |
* personal projects without any serious issues. |
|---|
| 16 |
* |
|---|
| 17 |
* Main Numpy license: |
|---|
| 18 |
* |
|---|
| 19 |
* Copyright (c) 2005-2009, NumPy Developers. |
|---|
| 20 |
* All rights reserved. |
|---|
| 21 |
* |
|---|
| 22 |
* Redistribution and use in source and binary forms, with or without |
|---|
| 23 |
* modification, are permitted provided that the following conditions are |
|---|
| 24 |
* met: |
|---|
| 25 |
* |
|---|
| 26 |
* * Redistributions of source code must retain the above copyright |
|---|
| 27 |
* notice, this list of conditions and the following disclaimer. |
|---|
| 28 |
* |
|---|
| 29 |
* * Redistributions in binary form must reproduce the above |
|---|
| 30 |
* copyright notice, this list of conditions and the following |
|---|
| 31 |
* disclaimer in the documentation and/or other materials provided |
|---|
| 32 |
* with the distribution. |
|---|
| 33 |
* |
|---|
| 34 |
* * Neither the name of the NumPy Developers nor the names of any |
|---|
| 35 |
* contributors may be used to endorse or promote products derived |
|---|
| 36 |
* from this software without specific prior written permission. |
|---|
| 37 |
* |
|---|
| 38 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 39 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 40 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|---|
| 41 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|---|
| 42 |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|---|
| 43 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|---|
| 44 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|---|
| 45 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|---|
| 46 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 47 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|---|
| 48 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 49 |
* |
|---|
| 50 |
* distribution.c license: |
|---|
| 51 |
* |
|---|
| 52 |
* Copyright 2005 Robert Kern (robert.kern@gmail.com) |
|---|
| 53 |
* |
|---|
| 54 |
* Permission is hereby granted, free of charge, to any person obtaining a |
|---|
| 55 |
* copy of this software and associated documentation files (the |
|---|
| 56 |
* "Software"), to deal in the Software without restriction, including |
|---|
| 57 |
* without limitation the rights to use, copy, modify, merge, publish, |
|---|
| 58 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|---|
| 59 |
* permit persons to whom the Software is furnished to do so, subject to |
|---|
| 60 |
* the following conditions: |
|---|
| 61 |
* |
|---|
| 62 |
* The above copyright notice and this permission notice shall be included |
|---|
| 63 |
* in all copies or substantial portions of the Software. |
|---|
| 64 |
* |
|---|
| 65 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|---|
| 66 |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|---|
| 67 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|---|
| 68 |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|---|
| 69 |
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|---|
| 70 |
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|---|
| 71 |
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|---|
| 72 |
*/ |
|---|
| 73 |
|
|---|
| 74 |
/* The implementations of rHypergeometricHyp() and rHypergeometricHrua() |
|---|
| 75 |
* were adapted from Ivan Frohne's rv.py which has this |
|---|
| 76 |
* license: |
|---|
| 77 |
* |
|---|
| 78 |
* Copyright 1998 by Ivan Frohne; Wasilla, Alaska, U.S.A. |
|---|
| 79 |
* All Rights Reserved |
|---|
| 80 |
* |
|---|
| 81 |
* Permission to use, copy, modify and distribute this software and its |
|---|
| 82 |
* documentation for any purpose, free of charge, is granted subject to the |
|---|
| 83 |
* following conditions: |
|---|
| 84 |
* The above copyright notice and this permission notice shall be included in |
|---|
| 85 |
* all copies or substantial portions of the software. |
|---|
| 86 |
* |
|---|
| 87 |
* THE SOFTWARE AND DOCUMENTATION IS PROVIDED WITHOUT WARRANTY OF ANY KIND, |
|---|
| 88 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY, FITNESS |
|---|
| 89 |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR |
|---|
| 90 |
* OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM OR DAMAGES IN A CONTRACT |
|---|
| 91 |
* ACTION, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|---|
| 92 |
* SOFTWARE OR ITS DOCUMENTATION. |
|---|
| 93 |
*/ |
|---|
| 94 |
|
|---|
| 95 |
/* References: |
|---|
| 96 |
* |
|---|
| 97 |
* Devroye, Luc. _Non-Uniform Random Variate Generation_. |
|---|
| 98 |
* Springer-Verlag, New York, 1986. |
|---|
| 99 |
* http://cgm.cs.mcgill.ca/~luc/rnbookindex.html |
|---|
| 100 |
* |
|---|
| 101 |
* Kachitvichyanukul, V. and Schmeiser, B. W. Binomial Random Variate |
|---|
| 102 |
* Generation. Communications of the ACM, 31, 2 (February, 1988) 216. |
|---|
| 103 |
* |
|---|
| 104 |
* Hoermann, W. The Transformed Rejection Method for Generating Poisson Random |
|---|
| 105 |
* Variables. Insurance: Mathematics and Economics, (to appear) |
|---|
| 106 |
* http://citeseer.csail.mit.edu/151115.html |
|---|
| 107 |
* |
|---|
| 108 |
* Marsaglia, G. and Tsang, W. W. A Simple Method for Generating Gamma |
|---|
| 109 |
* Variables. ACM Transactions on Mathematical Software, Vol. 26, No. 3, |
|---|
| 110 |
* September 2000, Pages 363-372. |
|---|
| 111 |
*/ |
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 |
/* Unit tests are non-deterministic. They prove that the distributions |
|---|
| 115 |
* are reasonable by using K-S tests and summary stats, but cannot |
|---|
| 116 |
* deterministically prove correctness.*/ |
|---|
| 117 |
|
|---|
| 118 |
module dstats.random; |
|---|
| 119 |
|
|---|
| 120 |
import std.math, std.algorithm, dstats.distrib, std.traits, std.typetuple, |
|---|
| 121 |
std.exception; |
|---|
| 122 |
public import std.random; //For uniform distrib. |
|---|
| 123 |
|
|---|
| 124 |
import dstats.alloc, dstats.base; |
|---|
| 125 |
|
|---|
| 126 |
version(unittest) { |
|---|
| 127 |
import std.stdio, dstats.tests, dstats.summary, dstats.gamma, |
|---|
| 128 |
std.range; |
|---|
| 129 |
void main() {} |
|---|
| 130 |
} |
|---|
| 131 |
|
|---|
| 132 |
/**Convenience function to allow one-statement creation of arrays of random |
|---|
| 133 |
* numbers. |
|---|
| 134 |
* |
|---|
| 135 |
* Examples: |
|---|
| 136 |
* --- |
|---|
| 137 |
* // Create an array of 10 random numbers distributed Normal(0, 1). |
|---|
| 138 |
* auto normals = randArray!rNorm(10, 0, 1); |
|---|
| 139 |
* --- |
|---|
| 140 |
*/ |
|---|
| 141 |
auto randArray(alias randFun, Args...)(size_t N, Args args) { |
|---|
| 142 |
alias typeof(randFun(args)) R; |
|---|
| 143 |
return randArray!(R, randFun, Args)(N, args); |
|---|
| 144 |
} |
|---|
| 145 |
|
|---|
| 146 |
unittest { |
|---|
| 147 |
// Just check if it compiles. |
|---|
| 148 |
auto nums = randArray!rNorm(5, 0, 1); |
|---|
| 149 |
auto nums2 = randArray!rBinomial(10, 5, 0.5); |
|---|
| 150 |
} |
|---|
| 151 |
|
|---|
| 152 |
/**Allows the creation of an array of random numbers with an explicitly |
|---|
| 153 |
* specified type. Useful, for example, when single-precision floats are all |
|---|
| 154 |
* you need. |
|---|
| 155 |
* |
|---|
| 156 |
* Examples: |
|---|
| 157 |
* --- |
|---|
| 158 |
* // Create an array of 10 million floats distributed Normal(0, 1). |
|---|
| 159 |
* float[] normals = randArray!(float, rNorm)(10, 0, 1); |
|---|
| 160 |
* --- |
|---|
| 161 |
*/ |
|---|
| 162 |
R[] randArray(R, alias randFun, Args...)(size_t N, Args args) { |
|---|
| 163 |
auto ret = newVoid!(R)(N); |
|---|
| 164 |
foreach(ref elem; ret) { |
|---|
| 165 |
elem = randFun(args); |
|---|
| 166 |
} |
|---|
| 167 |
|
|---|
| 168 |
return ret; |
|---|
| 169 |
} |
|---|
| 170 |
|
|---|
| 171 |
/// |
|---|
| 172 |
struct RandRange(alias randFun, T...) { |
|---|
| 173 |
private: |
|---|
| 174 |
T args; |
|---|
| 175 |
double normData = double.nan; // TLS stuff for normal. |
|---|
| 176 |
typeof(randFun(args)) frontElem; |
|---|
| 177 |
public: |
|---|
| 178 |
enum bool empty = false; |
|---|
| 179 |
|
|---|
| 180 |
this(T args) { |
|---|
| 181 |
this.args = args; |
|---|
| 182 |
popFront; |
|---|
| 183 |
} |
|---|
| 184 |
|
|---|
| 185 |
@property typeof(randFun(args)) front() { |
|---|
| 186 |
return frontElem; |
|---|
| 187 |
} |
|---|
| 188 |
|
|---|
| 189 |
void popFront() { |
|---|
| 190 |
/* This is a kludge to make the contents of this range deterministic |
|---|
| 191 |
* given the state of the underlying random number generator without |
|---|
| 192 |
* a massive redesign. We store the state in this struct and |
|---|
| 193 |
* swap w/ the TLS data for rNorm on each call to popFront. This has to |
|---|
| 194 |
* be done no matter what distribution we're using b/c a lot of others |
|---|
| 195 |
* rely on the normal.*/ |
|---|
| 196 |
auto lastNormPtr = &lastNorm; // Cache ptr once, avoid repeated TLS lookup. |
|---|
| 197 |
auto temp = *lastNormPtr; // Store old state. |
|---|
| 198 |
*lastNormPtr = normData; // Replace it. |
|---|
| 199 |
this.frontElem = randFun(args); |
|---|
| 200 |
normData = *lastNormPtr; |
|---|
| 201 |
*lastNormPtr = temp; |
|---|
| 202 |
} |
|---|
| 203 |
|
|---|
| 204 |
@property typeof(this) save() { |
|---|
| 205 |
return this; |
|---|
| 206 |
} |
|---|
| 207 |
} |
|---|
| 208 |
|
|---|
| 209 |
/**Turn a random number generator function into an infinite range. |
|---|
| 210 |
* Params is a tuple of the distribution parameters. This is specified |
|---|
| 211 |
* in the same order as when calling the function directly. |
|---|
| 212 |
* |
|---|
| 213 |
* The sequence generated by this range is deterministic and repeatable given |
|---|
| 214 |
* the state of the underlying random number generator. If the underlying |
|---|
| 215 |
* random number generator is explicitly specified, as opposed to using the |
|---|
| 216 |
* default thread-local global RNG, it is copied when the struct is copied. |
|---|
| 217 |
* See below for an example of this behavior. |
|---|
| 218 |
* |
|---|
| 219 |
* Examples: |
|---|
| 220 |
* --- |
|---|
| 221 |
* // Print out some summary statistics for 10,000 Poisson-distributed |
|---|
| 222 |
* // random numbers w/ Poisson parameter 2. |
|---|
| 223 |
* auto gen = Random(unpredictableSeed); |
|---|
| 224 |
* auto pois1k = take(10_000, randRange!rPoisson(2, gen)); |
|---|
| 225 |
* writeln( summary(pois1k) ); |
|---|
| 226 |
* writeln( summary(pois1k) ); // Exact same results as first call. |
|---|
| 227 |
* --- |
|---|
| 228 |
*/ |
|---|
| 229 |
RandRange!(randFun, T) randRange(alias randFun, T...)(T params) { |
|---|
| 230 |
alias RandRange!(randFun, T) RT; |
|---|
| 231 |
RT ret; // Bypass the ctor b/c it's screwy. |
|---|
| 232 |
ret.args = params; |
|---|
| 233 |
ret.popFront; |
|---|
| 234 |
return ret; |
|---|
| 235 |
} |
|---|
| 236 |
|
|---|
| 237 |
unittest { |
|---|
| 238 |
// The thing to test here is that the results are deterministic given |
|---|
| 239 |
// an underlying RNG. |
|---|
| 240 |
|
|---|
| 241 |
{ |
|---|
| 242 |
auto norms = take(randRange!rNorm(0, 1, Random(unpredictableSeed)), 99); |
|---|
| 243 |
auto arr1 = toArray(norms); |
|---|
| 244 |
auto arr2 = toArray(norms); |
|---|
| 245 |
assert(arr1 == arr2); |
|---|
| 246 |
} |
|---|
| 247 |
|
|---|
| 248 |
{ |
|---|
| 249 |
auto binomSmall = take(randRange!rBinomial(20, 0.5, Random(unpredictableSeed)), 99); |
|---|
| 250 |
auto arr1 = toArray(binomSmall); |
|---|
| 251 |
auto arr2 = toArray(binomSmall); |
|---|
| 252 |
assert(arr1 == arr2); |
|---|
| 253 |
} |
|---|
| 254 |
|
|---|
| 255 |
{ |
|---|
| 256 |
auto binomLarge = take(randRange!rBinomial(20000, 0.4, Random(unpredictableSeed)), 99); |
|---|
| 257 |
auto arr1 = toArray(binomLarge); |
|---|
| 258 |
auto arr2 = toArray(binomLarge); |
|---|
| 259 |
assert(arr1 == arr2); |
|---|
| 260 |
} |
|---|
| 261 |
writeln("Passed RandRange test."); |
|---|
| 262 |
} |
|---|
| 263 |
|
|---|
| 264 |
// Thread local data for normal distrib. that is preserved across calls. |
|---|
| 265 |
private static double lastNorm = double.nan; |
|---|
| 266 |
|
|---|
| 267 |
/// |
|---|
| 268 |
double rNorm(RGen = Random)(double mean, double sd, ref RGen gen = rndGen) { |
|---|
| 269 |
dstatsEnforce(sd > 0, "Standard deviation must be > 0 for rNorm."); |
|---|
| 270 |
|
|---|
| 271 |
double lr = lastNorm; |
|---|
| 272 |
if (!isNaN(lr)) { |
|---|
| 273 |
lastNorm = double.nan; |
|---|
| 274 |
return lr * sd + mean; |
|---|
| 275 |
} |
|---|
| 276 |
|
|---|
| 277 |
double x1 = void, x2 = void, r2 = void; |
|---|
| 278 |
do { |
|---|
| 279 |
x1 = uniform(-1.0L, 1.0L, gen); |
|---|
| 280 |
x2 = uniform(-1.0L, 1.0L, gen); |
|---|
| 281 |
r2 = x1 * x1 + x2 * x2; |
|---|
| 282 |
} while (r2 > 1.0L || r2 == 0.0L); |
|---|
| 283 |
double f = sqrt(-2.0L * log(r2) / r2); |
|---|
| 284 |
lastNorm = f * x1; |
|---|
| 285 |
return f * x2 * sd + mean; |
|---|
| 286 |
} |
|---|
| 287 |
|
|---|
| 288 |
|
|---|
| 289 |
unittest { |
|---|
| 290 |
auto observ = randArray!rNorm(100_000, 0, 1); |
|---|
| 291 |
auto ksRes = ksTest(observ, parametrize!(normalCDF)(0.0L, 1.0L)); |
|---|
| 292 |
auto summ = summary(observ); |
|---|
| 293 |
|
|---|
| 294 |
writeln("100k samples from normal(0, 1): K-S P-val: ", ksRes.p); |
|---|
| 295 |
writeln("\tMean Expected: 0 Observed: ", summ.mean); |
|---|
| 296 |
writeln("\tMedian Expected: 0 Observed: ", median(observ)); |
|---|
| 297 |
writeln("\tStdev Expected: 1 Observed: ", summ.stdev); |
|---|
| 298 |
writeln("\tKurtosis Expected: 0 Observed: ", summ.kurtosis); |
|---|
| 299 |
writeln("\tSkewness Expected: 0 Observed: ", summ.skewness); |
|---|
| 300 |
} |
|---|
| 301 |
|
|---|
| 302 |
/// |
|---|
| 303 |
double rCauchy(RGen = Random)(double X0, double gamma, ref RGen gen = rndGen) { |
|---|
| 304 |
dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); |
|---|
| 305 |
|
|---|
| 306 |
return (rNorm(0, 1, gen) / rNorm(0, 1, gen)) * gamma + X0; |
|---|
| 307 |
} |
|---|
| 308 |
|
|---|
| 309 |
unittest { |
|---|
| 310 |
auto observ = randArray!rCauchy(100_000, 2, 5); |
|---|
| 311 |
auto ksRes = ksTest(observ, parametrize!(cauchyCDF)(2.0L, 5.0L)); |
|---|
| 312 |
|
|---|
| 313 |
auto summ = summary(observ); |
|---|
| 314 |
writeln("100k samples from Cauchy(2, 5): K-S P-val: ", ksRes.p); |
|---|
| 315 |
writeln("\tMean Expected: N/A Observed: ", summ.mean); |
|---|
| 316 |
writeln("\tMedian Expected: 2 Observed: ", median(observ)); |
|---|
| 317 |
writeln("\tStdev Expected: N/A Observed: ", summ.stdev); |
|---|
| 318 |
writeln("\tKurtosis Expected: N/A Observed: ", summ.kurtosis); |
|---|
| 319 |
writeln("\tSkewness Expected: N/A Observed: ", summ.skewness); |
|---|
| 320 |
} |
|---|
| 321 |
|
|---|
| 322 |
/// |
|---|
| 323 |
double rStudentT(RGen = Random)(double df, ref RGen gen = rndGen) { |
|---|
| 324 |
dstatsEnforce(df > 0, "Student's T distribution must have >0 degrees of freedom."); |
|---|
| 325 |
|
|---|
| 326 |
double N = rNorm(0, 1, gen); |
|---|
| 327 |
double G = stdGamma(df / 2, gen); |
|---|
| 328 |
double X = sqrt(df / 2) * N / sqrt(G); |
|---|
| 329 |
return X; |
|---|
| 330 |
} |
|---|
| 331 |
|
|---|
| 332 |
unittest { |
|---|
| 333 |
auto observ = randArray!rStudentT(100_000, 5); |
|---|
| 334 |
auto ksRes = ksTest(observ, parametrize!(studentsTCDF)(5)); |
|---|
| 335 |
|
|---|
| 336 |
auto summ = summary(observ); |
|---|
| 337 |
writeln("100k samples from T(5): K-S P-val: ", ksRes.p); |
|---|
| 338 |
writeln("\tMean Expected: 0 Observed: ", summ.mean); |
|---|
| 339 |
writeln("\tMedian Expected: 0 Observed: ", median(observ)); |
|---|
| 340 |
writeln("\tStdev Expected: 1.2909 Observed: ", summ.stdev); |
|---|
| 341 |
writeln("\tKurtosis Expected: 6 Observed: ", summ.kurtosis); |
|---|
| 342 |
writeln("\tSkewness Expected: 0 Observed: ", summ.skewness); |
|---|
| 343 |
} |
|---|
| 344 |
|
|---|
| 345 |
/// |
|---|
| 346 |
double rFisher(RGen = Random)(double df1, double df2, ref RGen gen = rndGen) { |
|---|
| 347 |
dstatsEnforce(df1 > 0 && df2 > 0, |
|---|
| 348 |
"df1 and df2 must be >0 for the Fisher distribution."); |
|---|
| 349 |
|
|---|
| 350 |
return (rChiSquare(df1, gen) * df2) / |
|---|
| 351 |
(rChiSquare(df2, gen) * df1); |
|---|
| 352 |
} |
|---|
| 353 |
|
|---|
| 354 |
unittest { |
|---|
| 355 |
auto observ = randArray!rFisher(100_000, 5, 7); |
|---|
| 356 |
auto ksRes = ksTest(observ, parametrize!(fisherCDF)(5, 7)); |
|---|
| 357 |
writeln("100k samples from fisher(5, 7): K-S P-val: ", ksRes.p); |
|---|
| 358 |
writeln("\tMean Expected: ", 7.0 / 5, " Observed: ", mean(observ)); |
|---|
| 359 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 360 |
writeln("\tStdev Expected: ?? Observed: ", stdev(observ)); |
|---|
| 361 |
writeln("\tKurtosis Expected: ?? Observed: ", kurtosis(observ)); |
|---|
| 362 |
writeln("\tSkewness Expected: ?? Observed: ", skewness(observ)); |
|---|
| 363 |
delete observ; |
|---|
| 364 |
} |
|---|
| 365 |
|
|---|
| 366 |
/// |
|---|
| 367 |
double rChiSquare(RGen = Random)(double df, ref RGen gen = rndGen) { |
|---|
| 368 |
dstatsEnforce(df > 0, "df must be > 0 for chiSquare distribution."); |
|---|
| 369 |
|
|---|
| 370 |
return 2.0 * stdGamma(df / 2.0L, gen); |
|---|
| 371 |
} |
|---|
| 372 |
|
|---|
| 373 |
unittest { |
|---|
| 374 |
double df = 5; |
|---|
| 375 |
double[] observ = new double[100_000]; |
|---|
| 376 |
foreach(ref elem; observ) |
|---|
| 377 |
elem = rChiSquare(df); |
|---|
| 378 |
auto ksRes = ksTest(observ, parametrize!(chiSqrCDF)(5)); |
|---|
| 379 |
writeln("100k samples from Chi-Square: K-S P-val: ", ksRes.p); |
|---|
| 380 |
writeln("\tMean Expected: ", df, " Observed: ", mean(observ)); |
|---|
| 381 |
writeln("\tMedian Expected: ", df - (2.0L / 3.0L), " Observed: ", median(observ)); |
|---|
| 382 |
writeln("\tStdev Expected: ", sqrt(2 * df), " Observed: ", stdev(observ)); |
|---|
| 383 |
writeln("\tKurtosis Expected: ", 12 / df, " Observed: ", kurtosis(observ)); |
|---|
| 384 |
writeln("\tSkewness Expected: ", sqrt(8 / df), " Observed: ", skewness(observ)); |
|---|
| 385 |
delete observ; |
|---|
| 386 |
} |
|---|
| 387 |
|
|---|
| 388 |
/// |
|---|
| 389 |
int rPoisson(RGen = Random)(double lam, ref RGen gen = rndGen) { |
|---|
| 390 |
dstatsEnforce(lam > 0, "lambda must be >0 for Poisson distribution."); |
|---|
| 391 |
|
|---|
| 392 |
static int poissonMult(ref RGen gen, double lam) { |
|---|
| 393 |
double U = void; |
|---|
| 394 |
|
|---|
| 395 |
double enlam = exp(-lam); |
|---|
| 396 |
int X = 0; |
|---|
| 397 |
double prod = 1.0; |
|---|
| 398 |
while (true) { |
|---|
| 399 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 400 |
prod *= U; |
|---|
| 401 |
if (prod > enlam) { |
|---|
| 402 |
X += 1; |
|---|
| 403 |
} else { |
|---|
| 404 |
return X; |
|---|
| 405 |
} |
|---|
| 406 |
} |
|---|
| 407 |
assert(0); |
|---|
| 408 |
} |
|---|
| 409 |
|
|---|
| 410 |
enum double LS2PI = 0.91893853320467267; |
|---|
| 411 |
enum double TWELFTH = 0.083333333333333333333333; |
|---|
| 412 |
static int poissonPtrs(ref RGen gen, double lam) { |
|---|
| 413 |
int k; |
|---|
| 414 |
double U = void, V = void, us = void; |
|---|
| 415 |
|
|---|
| 416 |
double slam = sqrt(lam); |
|---|
| 417 |
double loglam = log(lam); |
|---|
| 418 |
double b = 0.931 + 2.53*slam; |
|---|
| 419 |
double a = -0.059 + 0.02483*b; |
|---|
| 420 |
double invalpha = 1.1239 + 1.1328/(b-3.4); |
|---|
| 421 |
double vr = 0.9277 - 3.6224/(b-2); |
|---|
| 422 |
|
|---|
| 423 |
while (true) { |
|---|
| 424 |
U = uniform(-0.5L, 0.5L, gen); |
|---|
| 425 |
V = uniform(0.0L, 1.0L, gen); |
|---|
| 426 |
us = 0.5 - abs(U); |
|---|
| 427 |
k = cast(int) floor((2*a/us + b)*U + lam + 0.43); |
|---|
| 428 |
if ((us >= 0.07) && (V <= vr)) { |
|---|
| 429 |
return k; |
|---|
| 430 |
} |
|---|
| 431 |
if ((k < 0) || ((us < 0.013) && (V > us))) { |
|---|
| 432 |
continue; |
|---|
| 433 |
} |
|---|
| 434 |
if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <= |
|---|
| 435 |
(-lam + k*loglam - lgamma(k+1))) { |
|---|
| 436 |
return k; |
|---|
| 437 |
} |
|---|
| 438 |
} |
|---|
| 439 |
assert(0); |
|---|
| 440 |
} |
|---|
| 441 |
|
|---|
| 442 |
|
|---|
| 443 |
if (lam >= 10) { |
|---|
| 444 |
return poissonPtrs(gen, lam); |
|---|
| 445 |
} else if (lam == 0) { |
|---|
| 446 |
return 0; |
|---|
| 447 |
} else { |
|---|
| 448 |
return poissonMult(gen, lam); |
|---|
| 449 |
} |
|---|
| 450 |
} |
|---|
| 451 |
|
|---|
| 452 |
unittest { |
|---|
| 453 |
double lambda = 15L; |
|---|
| 454 |
int[] observ = new int[100_000]; |
|---|
| 455 |
foreach(ref elem; observ) |
|---|
| 456 |
elem = rPoisson(lambda); |
|---|
| 457 |
writeln("100k samples from poisson(", lambda, "):"); |
|---|
| 458 |
writeln("\tMean Expected: ", lambda, |
|---|
| 459 |
" Observed: ", mean(observ)); |
|---|
| 460 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 461 |
writeln("\tStdev Expected: ", sqrt(lambda), |
|---|
| 462 |
" Observed: ", stdev(observ)); |
|---|
| 463 |
writeln("\tKurtosis Expected: ", 1 / lambda, |
|---|
| 464 |
" Observed: ", kurtosis(observ)); |
|---|
| 465 |
writeln("\tSkewness Expected: ", 1 / sqrt(lambda), |
|---|
| 466 |
" Observed: ", skewness(observ)); |
|---|
| 467 |
delete observ; |
|---|
| 468 |
} |
|---|
| 469 |
|
|---|
| 470 |
/// |
|---|
| 471 |
int rBernoulli(RGen = Random)(double P = 0.5, ref RGen gen = rndGen) { |
|---|
| 472 |
dstatsEnforce(P >= 0 && P <= 1, "P must be between 0, 1 for Bernoulli distribution."); |
|---|
| 473 |
|
|---|
| 474 |
double pVal = uniform(0.0L, 1.0L, gen); |
|---|
| 475 |
return cast(int) (pVal <= P); |
|---|
| 476 |
} |
|---|
| 477 |
|
|---|
| 478 |
private struct BinoState { |
|---|
| 479 |
bool has_binomial; |
|---|
| 480 |
int nsave; |
|---|
| 481 |
double psave; |
|---|
| 482 |
int m; |
|---|
| 483 |
double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4; |
|---|
| 484 |
double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x; |
|---|
| 485 |
} |
|---|
| 486 |
|
|---|
| 487 |
private BinoState* binoState() { |
|---|
| 488 |
// Store BinoState structs on heap rather than directly in TLS. |
|---|
| 489 |
|
|---|
| 490 |
static BinoState* stateTLS; |
|---|
| 491 |
auto tlsPtr = stateTLS; |
|---|
| 492 |
if (tlsPtr is null) { |
|---|
| 493 |
tlsPtr = new BinoState; |
|---|
| 494 |
stateTLS = tlsPtr; |
|---|
| 495 |
} |
|---|
| 496 |
return tlsPtr; |
|---|
| 497 |
} |
|---|
| 498 |
|
|---|
| 499 |
|
|---|
| 500 |
private int rBinomialBtpe(RGen = Random)(int n, double p, ref RGen gen = rndGen) { |
|---|
| 501 |
auto state = binoState; |
|---|
| 502 |
double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4; |
|---|
| 503 |
double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x; |
|---|
| 504 |
int m,y,k,i; |
|---|
| 505 |
|
|---|
| 506 |
if (!(state.has_binomial) || |
|---|
| 507 |
(state.nsave != n) || |
|---|
| 508 |
(state.psave != p)) { |
|---|
| 509 |
/* initialize */ |
|---|
| 510 |
state.nsave = n; |
|---|
| 511 |
state.psave = p; |
|---|
| 512 |
state.has_binomial = 1; |
|---|
| 513 |
state.r = r = min(p, 1.0-p); |
|---|
| 514 |
state.q = q = 1.0 - r; |
|---|
| 515 |
state.fm = fm = n*r+r; |
|---|
| 516 |
state.m = m = cast(int)floor(state.fm); |
|---|
| 517 |
state.p1 = p1 = floor(2.195*sqrt(n*r*q)-4.6*q) + 0.5; |
|---|
| 518 |
state.xm = xm = m + 0.5; |
|---|
| 519 |
state.xl = xl = xm - p1; |
|---|
| 520 |
state.xr = xr = xm + p1; |
|---|
| 521 |
state.c = c = 0.134 + 20.5/(15.3 + m); |
|---|
| 522 |
a = (fm - xl)/(fm-xl*r); |
|---|
| 523 |
state.laml = laml = a*(1.0 + a/2.0); |
|---|
| 524 |
a = (xr - fm)/(xr*q); |
|---|
| 525 |
state.lamr = lamr = a*(1.0 + a/2.0); |
|---|
| 526 |
state.p2 = p2 = p1*(1.0 + 2.0*c); |
|---|
| 527 |
state.p3 = p3 = p2 + c/laml; |
|---|
| 528 |
state.p4 = p4 = p3 + c/lamr; |
|---|
| 529 |
} else { |
|---|
| 530 |
r = state.r; |
|---|
| 531 |
q = state.q; |
|---|
| 532 |
fm = state.fm; |
|---|
| 533 |
m = state.m; |
|---|
| 534 |
p1 = state.p1; |
|---|
| 535 |
xm = state.xm; |
|---|
| 536 |
xl = state.xl; |
|---|
| 537 |
xr = state.xr; |
|---|
| 538 |
c = state.c; |
|---|
| 539 |
laml = state.laml; |
|---|
| 540 |
lamr = state.lamr; |
|---|
| 541 |
p2 = state.p2; |
|---|
| 542 |
p3 = state.p3; |
|---|
| 543 |
p4 = state.p4; |
|---|
| 544 |
} |
|---|
| 545 |
|
|---|
| 546 |
/* sigh ... */ |
|---|
| 547 |
Step10: |
|---|
| 548 |
nrq = n*r*q; |
|---|
| 549 |
u = uniform(0.0L, p4, gen); |
|---|
| 550 |
v = uniform(0.0L, 1.0L, gen); |
|---|
| 551 |
if (u > p1) goto Step20; |
|---|
| 552 |
y = cast(int)floor(xm - p1*v + u); |
|---|
| 553 |
goto Step60; |
|---|
| 554 |
|
|---|
| 555 |
Step20: |
|---|
| 556 |
if (u > p2) goto Step30; |
|---|
| 557 |
x = xl + (u - p1)/c; |
|---|
| 558 |
v = v*c + 1.0 - fabs(m - x + 0.5)/p1; |
|---|
| 559 |
if (v > 1.0) goto Step10; |
|---|
| 560 |
y = cast(int)floor(x); |
|---|
| 561 |
goto Step50; |
|---|
| 562 |
|
|---|
| 563 |
Step30: |
|---|
| 564 |
if (u > p3) goto Step40; |
|---|
| 565 |
y = cast(int)floor(xl + log(v)/laml); |
|---|
| 566 |
if (y < 0) goto Step10; |
|---|
| 567 |
v = v*(u-p2)*laml; |
|---|
| 568 |
goto Step50; |
|---|
| 569 |
|
|---|
| 570 |
Step40: |
|---|
| 571 |
y = cast(int)floor(xr - log(v)/lamr); |
|---|
| 572 |
if (y > n) goto Step10; |
|---|
| 573 |
v = v*(u-p3)*lamr; |
|---|
| 574 |
|
|---|
| 575 |
Step50: |
|---|
| 576 |
k = cast(int) fabs(y - m); |
|---|
| 577 |
if ((k > 20) && (k < ((nrq)/2.0 - 1))) goto Step52; |
|---|
| 578 |
|
|---|
| 579 |
s = r/q; |
|---|
| 580 |
a = s*(n+1); |
|---|
| 581 |
F = 1.0; |
|---|
| 582 |
if (m < y) { |
|---|
| 583 |
for (i=m; i<=y; i++) { |
|---|
| 584 |
F *= (a/i - s); |
|---|
| 585 |
} |
|---|
| 586 |
} else if (m > y) { |
|---|
| 587 |
for (i=y; i<=m; i++) { |
|---|
| 588 |
F /= (a/i - s); |
|---|
| 589 |
} |
|---|
| 590 |
} else { |
|---|
| 591 |
if (v > F) goto Step10; |
|---|
| 592 |
goto Step60; |
|---|
| 593 |
} |
|---|
| 594 |
|
|---|
| 595 |
Step52: |
|---|
| 596 |
rho = (k/(nrq))*((k*(k/3.0 + 0.625) + 0.16666666666666666)/nrq + 0.5); |
|---|
| 597 |
t = -k*k/(2*nrq); |
|---|
| 598 |
A = log(v); |
|---|
| 599 |
if (A < (t - rho)) goto Step60; |
|---|
| 600 |
if (A > (t + rho)) goto Step10; |
|---|
| 601 |
|
|---|
| 602 |
x1 = y+1; |
|---|
| 603 |
f1 = m+1; |
|---|
| 604 |
z = n+1-m; |
|---|
| 605 |
w = n-y+1; |
|---|
| 606 |
x2 = x1*x1; |
|---|
| 607 |
f2 = f1*f1; |
|---|
| 608 |
z2 = z*z; |
|---|
| 609 |
w2 = w*w; |
|---|
| 610 |
if (A > (xm*log(f1/x1) |
|---|
| 611 |
+ (n-m+0.5)*log(z/w) |
|---|
| 612 |
+ (y-m)*log(w*r/(x1*q)) |
|---|
| 613 |
+ (13680.-(462.-(132.-(99.-140./f2)/f2)/f2)/f2)/f1/166320. |
|---|
| 614 |
+ (13680.-(462.-(132.-(99.-140./z2)/z2)/z2)/z2)/z/166320. |
|---|
| 615 |
+ (13680.-(462.-(132.-(99.-140./x2)/x2)/x2)/x2)/x1/166320. |
|---|
| 616 |
+ (13680.-(462.-(132.-(99.-140./w2)/w2)/w2)/w2)/w/166320.)) { |
|---|
| 617 |
goto Step10; |
|---|
| 618 |
} |
|---|
| 619 |
|
|---|
| 620 |
Step60: |
|---|
| 621 |
if (p > 0.5) { |
|---|
| 622 |
y = n - y; |
|---|
| 623 |
} |
|---|
| 624 |
|
|---|
| 625 |
return y; |
|---|
| 626 |
} |
|---|
| 627 |
|
|---|
| 628 |
private int rBinomialInversion(RGen = Random)(int n, double p, ref RGen gen = rndGen) { |
|---|
| 629 |
auto state = binoState; |
|---|
| 630 |
double q, qn, np, px, U; |
|---|
| 631 |
int X, bound; |
|---|
| 632 |
|
|---|
| 633 |
if (!(state.has_binomial) || |
|---|
| 634 |
(state.nsave != n) || |
|---|
| 635 |
(state.psave != p)) { |
|---|
| 636 |
state.nsave = n; |
|---|
| 637 |
state.psave = p; |
|---|
| 638 |
state.has_binomial = 1; |
|---|
| 639 |
state.q = q = 1.0 - p; |
|---|
| 640 |
state.r = qn = exp(n * log(q)); |
|---|
| 641 |
state.c = np = n*p; |
|---|
| 642 |
state.m = bound = cast(int) min(n, np + 10.0*sqrt(np*q + 1)); |
|---|
| 643 |
} else { |
|---|
| 644 |
q = state.q; |
|---|
| 645 |
qn = state.r; |
|---|
| 646 |
np = state.c; |
|---|
| 647 |
bound = cast(int) state.m; |
|---|
| 648 |
} |
|---|
| 649 |
X = 0; |
|---|
| 650 |
px = qn; |
|---|
| 651 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 652 |
while (U > px) { |
|---|
| 653 |
X++; |
|---|
| 654 |
if (X > bound) { |
|---|
| 655 |
X = 0; |
|---|
| 656 |
px = qn; |
|---|
| 657 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 658 |
} else { |
|---|
| 659 |
U -= px; |
|---|
| 660 |
px = ((n-X+1) * p * px)/(X*q); |
|---|
| 661 |
} |
|---|
| 662 |
} |
|---|
| 663 |
return X; |
|---|
| 664 |
} |
|---|
| 665 |
|
|---|
| 666 |
/// |
|---|
| 667 |
int rBinomial(RGen = Random)(int n, double p, ref RGen gen = rndGen) { |
|---|
| 668 |
dstatsEnforce(n >= 0, "n must be >= 0 for binomial distribution."); |
|---|
| 669 |
dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial distribution."); |
|---|
| 670 |
|
|---|
| 671 |
if (p <= 0.5) { |
|---|
| 672 |
if (p*n <= 30.0) { |
|---|
| 673 |
return rBinomialInversion(n, p, gen); |
|---|
| 674 |
} else { |
|---|
| 675 |
return rBinomialBtpe(n, p, gen); |
|---|
| 676 |
} |
|---|
| 677 |
} else { |
|---|
| 678 |
double q = 1.0-p; |
|---|
| 679 |
if (q*n <= 30.0) { |
|---|
| 680 |
return n - rBinomialInversion(n, q, gen); |
|---|
| 681 |
} else { |
|---|
| 682 |
return n - rBinomialBtpe(n, q, gen); |
|---|
| 683 |
} |
|---|
| 684 |
} |
|---|
| 685 |
} |
|---|
| 686 |
|
|---|
| 687 |
unittest { |
|---|
| 688 |
void testBinom(int n, double p) { |
|---|
| 689 |
int[] observ = new int[100_000]; |
|---|
| 690 |
foreach(ref elem; observ) |
|---|
| 691 |
elem = rBinomial(n, p); |
|---|
| 692 |
writeln("100k samples from binom.(", n, ", ", p, "):"); |
|---|
| 693 |
writeln("\tMean Expected: ", n * p, |
|---|
| 694 |
" Observed: ", mean(observ)); |
|---|
| 695 |
writeln("\tMedian Expected: ", n * p, " Observed: ", median(observ)); |
|---|
| 696 |
writeln("\tStdev Expected: ", sqrt(n * p * (1 - p)), |
|---|
| 697 |
" Observed: ", stdev(observ)); |
|---|
| 698 |
writeln("\tKurtosis Expected: ", (1 - 6 * p * (1 - p)) / (n * p * (1 - p)), |
|---|
| 699 |
" Observed: ", kurtosis(observ)); |
|---|
| 700 |
writeln("\tSkewness Expected: ", (1 - 2 * p) / (sqrt(n * p * (1 - p))), |
|---|
| 701 |
" Observed: ", skewness(observ)); |
|---|
| 702 |
delete observ; |
|---|
| 703 |
} |
|---|
| 704 |
|
|---|
| 705 |
testBinom(1000, 0.6); |
|---|
| 706 |
testBinom(3, 0.7); |
|---|
| 707 |
} |
|---|
| 708 |
|
|---|
| 709 |
private int hypergeoHyp(RGen = Random)(int good, int bad, int sample, ref RGen gen = rndGen) { |
|---|
| 710 |
int Z = void; |
|---|
| 711 |
double U = void; |
|---|
| 712 |
|
|---|
| 713 |
int d1 = bad + good - sample; |
|---|
| 714 |
double d2 = cast(double)min(bad, good); |
|---|
| 715 |
|
|---|
| 716 |
double Y = d2; |
|---|
| 717 |
int K = sample; |
|---|
| 718 |
while (Y > 0.0) { |
|---|
| 719 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 720 |
Y -= cast(int)floor(U + Y/(d1 + K)); |
|---|
| 721 |
K--; |
|---|
| 722 |
if (K == 0) break; |
|---|
| 723 |
} |
|---|
| 724 |
Z = cast(int)(d2 - Y); |
|---|
| 725 |
if (good > bad) Z = sample - Z; |
|---|
| 726 |
return Z; |
|---|
| 727 |
} |
|---|
| 728 |
|
|---|
| 729 |
private enum double D1 = 1.7155277699214135; |
|---|
| 730 |
private enum double D2 = 0.8989161620588988; |
|---|
| 731 |
private int hypergeoHrua(RGen = Random)(int good, int bad, int sample, ref RGen gen = rndGen) { |
|---|
| 732 |
int Z = void; |
|---|
| 733 |
double T = void, W = void, X = void, Y = void; |
|---|
| 734 |
|
|---|
| 735 |
int mingoodbad = min(good, bad); |
|---|
| 736 |
int popsize = good + bad; |
|---|
| 737 |
int maxgoodbad = max(good, bad); |
|---|
| 738 |
int m = min(sample, popsize - sample); |
|---|
| 739 |
double d4 = (cast(double)mingoodbad) / popsize; |
|---|
| 740 |
double d5 = 1.0 - d4; |
|---|
| 741 |
double d6 = m*d4 + 0.5; |
|---|
| 742 |
double d7 = sqrt((popsize - m) * sample * d4 *d5 / (popsize-1) + 0.5); |
|---|
| 743 |
double d8 = D1*d7 + D2; |
|---|
| 744 |
int d9 = cast(int)floor(cast(double)((m+1)*(mingoodbad+1))/(popsize+2)); |
|---|
| 745 |
double d10 = (lgamma(d9+1) + lgamma(mingoodbad-d9+1) + lgamma(m-d9+1) + |
|---|
| 746 |
lgamma(maxgoodbad-m+d9+1)); |
|---|
| 747 |
double d11 = min(min(m, mingoodbad)+1.0, floor(d6+16*d7)); |
|---|
| 748 |
/* 16 for 16-decimal-digit precision in D1 and D2 */ |
|---|
| 749 |
|
|---|
| 750 |
while (true) { |
|---|
| 751 |
X = uniform(0.0L, 1.0L, gen); |
|---|
| 752 |
Y = uniform(0.0L, 1.0L, gen); |
|---|
| 753 |
W = d6 + d8*(Y- 0.5)/X; |
|---|
| 754 |
|
|---|
| 755 |
/* fast rejection: */ |
|---|
| 756 |
if ((W < 0.0) || (W >= d11)) continue; |
|---|
| 757 |
|
|---|
| 758 |
Z = cast(int)floor(W); |
|---|
| 759 |
T = d10 - (lgamma(Z+1) + lgamma(mingoodbad-Z+1) + lgamma(m-Z+1) + |
|---|
| 760 |
lgamma(maxgoodbad-m+Z+1)); |
|---|
| 761 |
|
|---|
| 762 |
/* fast acceptance: */ |
|---|
| 763 |
if ((X*(4.0-X)-3.0) <= T) break; |
|---|
| 764 |
|
|---|
| 765 |
/* fast rejection: */ |
|---|
| 766 |
if (X*(X-T) >= 1) continue; |
|---|
| 767 |
|
|---|
| 768 |
if (2.0*log(X) <= T) break; /* acceptance */ |
|---|
| 769 |
} |
|---|
| 770 |
|
|---|
| 771 |
/* this is a correction to HRUA* by Ivan Frohne in rv.py */ |
|---|
| 772 |
if (good > bad) Z = m - Z; |
|---|
| 773 |
|
|---|
| 774 |
/* another fix from rv.py to allow sample to exceed popsize/2 */ |
|---|
| 775 |
if (m < sample) Z = good - Z; |
|---|
| 776 |
|
|---|
| 777 |
return Z; |
|---|
| 778 |
} |
|---|
| 779 |
|
|---|
| 780 |
/// |
|---|
| 781 |
int rHypergeometric(RGen = Random)(int n1, int n2, int n, ref RGen gen = rndGen) { |
|---|
| 782 |
dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 for hypergeometric distribution."); |
|---|
| 783 |
dstatsEnforce(n1 >= 0 && n2 >= 0 && n >= 0, |
|---|
| 784 |
"n, n1, n2 must be >= 0 for hypergeometric distribution."); |
|---|
| 785 |
|
|---|
| 786 |
alias n1 good; |
|---|
| 787 |
alias n2 bad; |
|---|
| 788 |
alias n sample; |
|---|
| 789 |
if (sample > 10) { |
|---|
| 790 |
return hypergeoHrua(good, bad, sample, gen); |
|---|
| 791 |
} else { |
|---|
| 792 |
return hypergeoHyp(good, bad, sample, gen); |
|---|
| 793 |
} |
|---|
| 794 |
} |
|---|
| 795 |
|
|---|
| 796 |
unittest { |
|---|
| 797 |
|
|---|
| 798 |
static double hyperStdev(int n1, int n2, int n) { |
|---|
| 799 |
return sqrt(cast(double) n * (cast(double) n1 / (n1 + n2)) |
|---|
| 800 |
* (1 - cast(double) n1 / (n1 + n2)) * (n1 + n2 - n) / (n1 + n2 - 1)); |
|---|
| 801 |
} |
|---|
| 802 |
|
|---|
| 803 |
static double hyperSkew(double n1, double n2, double n) { |
|---|
| 804 |
double N = n1 + n2; |
|---|
| 805 |
alias n1 m; |
|---|
| 806 |
double numer = (N - 2 * m) * sqrt(N - 1) * (N - 2 * n); |
|---|
| 807 |
double denom = sqrt(n * m * (N - m) * (N - n)) * (N - 2); |
|---|
| 808 |
return numer / denom; |
|---|
| 809 |
} |
|---|
| 810 |
|
|---|
| 811 |
void testHyper(int n1, int n2, int n) { |
|---|
| 812 |
int[] observ = new int[100_000]; |
|---|
| 813 |
foreach(ref elem; observ) |
|---|
| 814 |
elem = rHypergeometric(n1, n2, n); |
|---|
| 815 |
auto ksRes = ksTest(observ, parametrize!(hypergeometricCDF)(n1, n2, n)); |
|---|
| 816 |
writeln("100k samples from hypergeom.(", n1, ", ", n2, ", ", n, "):"); |
|---|
| 817 |
writeln("\tMean Expected: ", n * cast(double) n1 / (n1 + n2), |
|---|
| 818 |
" Observed: ", mean(observ)); |
|---|
| 819 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 820 |
writeln("\tStdev Expected: ", hyperStdev(n1, n2, n), |
|---|
| 821 |
" Observed: ", stdev(observ)); |
|---|
| 822 |
writeln("\tKurtosis Expected: ?? Observed: ", kurtosis(observ)); |
|---|
| 823 |
writeln("\tSkewness Expected: ", hyperSkew(n1, n2, n), " Observed: ", skewness(observ)); |
|---|
| 824 |
delete observ; |
|---|
| 825 |
} |
|---|
| 826 |
|
|---|
| 827 |
testHyper(4, 5, 2); |
|---|
| 828 |
testHyper(120, 105, 70); |
|---|
| 829 |
} |
|---|
| 830 |
|
|---|
| 831 |
private int rGeomSearch(RGen = Random)(double p, ref RGen gen = rndGen) { |
|---|
| 832 |
int X = 1; |
|---|
| 833 |
double sum = p, prod = p; |
|---|
| 834 |
double q = 1.0 - p; |
|---|
| 835 |
double U = uniform(0.0L, 1.0L, gen); |
|---|
| 836 |
while (U > sum) { |
|---|
| 837 |
prod *= q; |
|---|
| 838 |
sum += prod; |
|---|
| 839 |
X++; |
|---|
| 840 |
} |
|---|
| 841 |
return X; |
|---|
| 842 |
} |
|---|
| 843 |
|
|---|
| 844 |
private int rGeomInvers(RGen = Random)(double p, ref RGen gen = rndGen) { |
|---|
| 845 |
return cast(int)ceil(log(1.0-uniform(0.0L, 1.0L, gen))/log(1.0-p)); |
|---|
| 846 |
} |
|---|
| 847 |
|
|---|
| 848 |
int rGeometric(RGen = Random)(double p, ref RGen gen = rndGen) { |
|---|
| 849 |
dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for geometric distribution."); |
|---|
| 850 |
|
|---|
| 851 |
if (p >= 0.333333333333333333333333) { |
|---|
| 852 |
return rGeomSearch(p, gen); |
|---|
| 853 |
} else { |
|---|
| 854 |
return rGeomInvers(p, gen); |
|---|
| 855 |
} |
|---|
| 856 |
} |
|---|
| 857 |
|
|---|
| 858 |
unittest { |
|---|
| 859 |
|
|---|
| 860 |
void testGeom(double p) { |
|---|
| 861 |
int[] observ = new int[100_000]; |
|---|
| 862 |
foreach(ref elem; observ) |
|---|
| 863 |
elem = rGeometric(p); |
|---|
| 864 |
writeln("100k samples from geometric.(", p, "):"); |
|---|
| 865 |
writeln("\tMean Expected: ", 1 / p, |
|---|
| 866 |
" Observed: ", mean(observ)); |
|---|
| 867 |
writeln("\tMedian Expected: ", ceil(-log(2) / log(1 - p)), |
|---|
| 868 |
" Observed: ", median(observ)); |
|---|
| 869 |
writeln("\tStdev Expected: ", sqrt((1 - p) / (p * p)), |
|---|
| 870 |
" Observed: ", stdev(observ)); |
|---|
| 871 |
writeln("\tKurtosis Expected: ", 6 + (p * p) / (1 - p), |
|---|
| 872 |
" Observed: ", kurtosis(observ)); |
|---|
| 873 |
writeln("\tSkewness Expected: ", (2 - p) / sqrt(1 - p), |
|---|
| 874 |
" Observed: ", skewness(observ)); |
|---|
| 875 |
delete observ; |
|---|
| 876 |
} |
|---|
| 877 |
|
|---|
| 878 |
testGeom(0.1); |
|---|
| 879 |
testGeom(0.74); |
|---|
| 880 |
|
|---|
| 881 |
} |
|---|
| 882 |
|
|---|
| 883 |
/// |
|---|
| 884 |
int rNegBinom(RGen = Random)(double n, double p, ref RGen gen = rndGen) { |
|---|
| 885 |
dstatsEnforce(n >= 0, "n must be >= 0 for negative binomial distribution."); |
|---|
| 886 |
dstatsEnforce(p >= 0 && p <= 1, |
|---|
| 887 |
"p must be between 0, 1 for negative binomial distribution."); |
|---|
| 888 |
|
|---|
| 889 |
double Y = stdGamma(n, gen); |
|---|
| 890 |
Y *= (1 - p) / p; |
|---|
| 891 |
return rPoisson(Y, gen); |
|---|
| 892 |
} |
|---|
| 893 |
|
|---|
| 894 |
unittest { |
|---|
| 895 |
Random gen; |
|---|
| 896 |
gen.seed(unpredictableSeed); |
|---|
| 897 |
double p = 0.3L; |
|---|
| 898 |
int n = 30; |
|---|
| 899 |
int[] observ = new int[100_000]; |
|---|
| 900 |
foreach(ref elem; observ) |
|---|
| 901 |
elem = rNegBinom(n, p); |
|---|
| 902 |
writeln("100k samples from neg. binom.(", n,", ", p, "):"); |
|---|
| 903 |
writeln("\tMean Expected: ", n * (1 - p) / p, |
|---|
| 904 |
" Observed: ", mean(observ)); |
|---|
| 905 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 906 |
writeln("\tStdev Expected: ", sqrt(n * (1 - p) / (p * p)), |
|---|
| 907 |
" Observed: ", stdev(observ)); |
|---|
| 908 |
writeln("\tKurtosis Expected: ", (6 - p * (6 - p)) / (n * (1 - p)), |
|---|
| 909 |
" Observed: ", kurtosis(observ)); |
|---|
| 910 |
writeln("\tSkewness Expected: ", (2 - p) / sqrt(n * (1 - p)), |
|---|
| 911 |
" Observed: ", skewness(observ)); |
|---|
| 912 |
delete observ; |
|---|
| 913 |
} |
|---|
| 914 |
|
|---|
| 915 |
/// |
|---|
| 916 |
double rLaplace(RGen = Random)(double mu = 0, double b = 1, ref RGen gen = rndGen) { |
|---|
| 917 |
dstatsEnforce(b > 0, "b must be > 0 for Laplace distribution."); |
|---|
| 918 |
|
|---|
| 919 |
double p = uniform(0.0L, 1.0L, gen); |
|---|
| 920 |
return invLaplaceCDF(p, mu, b); |
|---|
| 921 |
} |
|---|
| 922 |
|
|---|
| 923 |
unittest { |
|---|
| 924 |
Random gen; |
|---|
| 925 |
gen.seed(unpredictableSeed); |
|---|
| 926 |
double[] observ = new double[100_000]; |
|---|
| 927 |
foreach(ref elem; observ) |
|---|
| 928 |
elem = rLaplace(); |
|---|
| 929 |
auto ksRes = ksTest(observ, parametrize!(laplaceCDF)(0.0L, 1.0L)); |
|---|
| 930 |
writeln("100k samples from Laplace(0, 1): K-S P-val: ", ksRes.p); |
|---|
| 931 |
writeln("\tMean Expected: 0 Observed: ", mean(observ)); |
|---|
| 932 |
writeln("\tMedian Expected: 0 Observed: ", median(observ)); |
|---|
| 933 |
writeln("\tStdev Expected: 1.414 Observed: ", stdev(observ)); |
|---|
| 934 |
writeln("\tKurtosis Expected: 3 Observed: ", kurtosis(observ)); |
|---|
| 935 |
writeln("\tSkewness Expected: 0 Observed: ", skewness(observ)); |
|---|
| 936 |
delete observ; |
|---|
| 937 |
} |
|---|
| 938 |
|
|---|
| 939 |
/// |
|---|
| 940 |
double rExponential(RGen = Random)(double lambda, ref RGen gen = rndGen) { |
|---|
| 941 |
dstatsEnforce(lambda > 0, "lambda must be > 0 for exponential distribution."); |
|---|
| 942 |
|
|---|
| 943 |
double p = uniform(0.0L, 1.0L, gen); |
|---|
| 944 |
return -log(p) / lambda; |
|---|
| 945 |
} |
|---|
| 946 |
|
|---|
| 947 |
unittest { |
|---|
| 948 |
double[] observ = new double[100_000]; |
|---|
| 949 |
foreach(ref elem; observ) |
|---|
| 950 |
elem = rExponential(2.0L); |
|---|
| 951 |
auto ksRes = ksTest(observ, parametrize!(gammaCDF)(2, 1)); |
|---|
| 952 |
writeln("100k samples from exponential(2): K-S P-val: ", ksRes.p); |
|---|
| 953 |
writeln("\tMean Expected: 0.5 Observed: ", mean(observ)); |
|---|
| 954 |
writeln("\tMedian Expected: 0.3465 Observed: ", median(observ)); |
|---|
| 955 |
writeln("\tStdev Expected: 0.5 Observed: ", stdev(observ)); |
|---|
| 956 |
writeln("\tKurtosis Expected: 6 Observed: ", kurtosis(observ)); |
|---|
| 957 |
writeln("\tSkewness Expected: 2 Observed: ", skewness(observ)); |
|---|
| 958 |
delete observ; |
|---|
| 959 |
} |
|---|
| 960 |
|
|---|
| 961 |
private double stdGamma(RGen = Random)(double shape, ref RGen gen) { |
|---|
| 962 |
double b = void, c = void; |
|---|
| 963 |
double U = void, V = void, X = void, Y = void; |
|---|
| 964 |
|
|---|
| 965 |
if (shape == 1.0) { |
|---|
| 966 |
return rExponential(1.0, gen); |
|---|
| 967 |
} else if (shape < 1.0) { |
|---|
| 968 |
for (;;) { |
|---|
| 969 |
U = uniform(0.0L, 1.0, gen); |
|---|
| 970 |
V = rExponential(1.0, gen); |
|---|
| 971 |
if (U <= 1.0 - shape) { |
|---|
| 972 |
X = pow(U, 1.0/shape); |
|---|
| 973 |
if (X <= V) { |
|---|
| 974 |
return X; |
|---|
| 975 |
} |
|---|
| 976 |
} else { |
|---|
| 977 |
Y = -log((1-U)/shape); |
|---|
| 978 |
X = pow(1.0 - shape + shape*Y, 1./shape); |
|---|
| 979 |
if (X <= (V + Y)) { |
|---|
| 980 |
return X; |
|---|
| 981 |
} |
|---|
| 982 |
} |
|---|
| 983 |
} |
|---|
| 984 |
} else { |
|---|
| 985 |
b = shape - 1./3.; |
|---|
| 986 |
c = 1./sqrt(9*b); |
|---|
| 987 |
for (;;) { |
|---|
| 988 |
do { |
|---|
| 989 |
X = rNorm(0.0L, 1.0L, gen); |
|---|
| 990 |
V = 1.0 + c*X; |
|---|
| 991 |
} while (V <= 0.0); |
|---|
| 992 |
|
|---|
| 993 |
V = V*V*V; |
|---|
| 994 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 995 |
if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V); |
|---|
| 996 |
if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V); |
|---|
| 997 |
} |
|---|
| 998 |
} |
|---|
| 999 |
} |
|---|
| 1000 |
|
|---|
| 1001 |
/// |
|---|
| 1002 |
double rGamma(RGen = Random)(double a, double b, ref RGen gen = rndGen) { |
|---|
| 1003 |
dstatsEnforce(a > 0, "a must be > 0 for gamma distribution."); |
|---|
| 1004 |
dstatsEnforce(b > 0, "b must be > 0 for gamma distribution."); |
|---|
| 1005 |
|
|---|
| 1006 |
return stdGamma(b, gen) / a; |
|---|
| 1007 |
} |
|---|
| 1008 |
|
|---|
| 1009 |
unittest { |
|---|
| 1010 |
double[] observ = new double[100_000]; |
|---|
| 1011 |
foreach(ref elem; observ) |
|---|
| 1012 |
elem = rGamma(2.0L, 3.0L); |
|---|
| 1013 |
auto ksRes = ksTest(observ, parametrize!(gammaCDF)(2, 3)); |
|---|
| 1014 |
writeln("100k samples from gamma(2, 3): K-S P-val: ", ksRes.p); |
|---|
| 1015 |
writeln("\tMean Expected: 1.5 Observed: ", mean(observ)); |
|---|
| 1016 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 1017 |
writeln("\tStdev Expected: 0.866 Observed: ", stdev(observ)); |
|---|
| 1018 |
writeln("\tKurtosis Expected: 2 Observed: ", kurtosis(observ)); |
|---|
| 1019 |
writeln("\tSkewness Expected: 1.15 Observed: ", skewness(observ)); |
|---|
| 1020 |
delete observ; |
|---|
| 1021 |
} |
|---|
| 1022 |
|
|---|
| 1023 |
/// |
|---|
| 1024 |
double rBeta(RGen = Random)(double a, double b, ref RGen gen = rndGen) { |
|---|
| 1025 |
dstatsEnforce(a > 0, "a must be > 0 for beta distribution."); |
|---|
| 1026 |
dstatsEnforce(b > 0, "b must be > 0 for beta distribution."); |
|---|
| 1027 |
|
|---|
| 1028 |
double Ga = void, Gb = void; |
|---|
| 1029 |
|
|---|
| 1030 |
if ((a <= 1.0) && (b <= 1.0)) { |
|---|
| 1031 |
double U, V, X, Y; |
|---|
| 1032 |
/* Use Jonk's algorithm */ |
|---|
| 1033 |
|
|---|
| 1034 |
while (1) { |
|---|
| 1035 |
U = uniform(0.0L, 1.0L, gen); |
|---|
| 1036 |
V = uniform(0.0L, 1.0L, gen); |
|---|
| 1037 |
X = pow(U, 1.0/a); |
|---|
| 1038 |
Y = pow(V, 1.0/b); |
|---|
| 1039 |
|
|---|
| 1040 |
if ((X + Y) <= 1.0) { |
|---|
| 1041 |
return X / (X + Y); |
|---|
| 1042 |
} |
|---|
| 1043 |
} |
|---|
| 1044 |
} else { |
|---|
| 1045 |
Ga = stdGamma(a, gen); |
|---|
| 1046 |
Gb = stdGamma(b, gen); |
|---|
| 1047 |
return Ga/(Ga + Gb); |
|---|
| 1048 |
} |
|---|
| 1049 |
assert(0); |
|---|
| 1050 |
} |
|---|
| 1051 |
|
|---|
| 1052 |
unittest { |
|---|
| 1053 |
double delegate(double) paramBeta(double a, double b) { |
|---|
| 1054 |
double parametrizedBeta(double x) { |
|---|
| 1055 |
return betaIncomplete(a, b, x); |
|---|
| 1056 |
} |
|---|
| 1057 |
return ¶metrizedBeta; |
|---|
| 1058 |
} |
|---|
| 1059 |
|
|---|
| 1060 |
static double betaStdev(double a, double b) { |
|---|
| 1061 |
return sqrt(a * b / ((a + b) * (a + b) * (a + b + 1))); |
|---|
| 1062 |
} |
|---|
| 1063 |
|
|---|
| 1064 |
static double betaSkew(double a, double b) { |
|---|
| 1065 |
auto numer = 2 * (b - a) * sqrt(a + b + 1); |
|---|
| 1066 |
auto denom = (a + b + 2) * sqrt(a * b); |
|---|
| 1067 |
return numer / denom; |
|---|
| 1068 |
} |
|---|
| 1069 |
|
|---|
| 1070 |
static double betaKurtosis(double a, double b) { |
|---|
| 1071 |
double numer = a * a * a - a * a * (2 * b - 1) + b * b * (b + 1) - 2 * a * b * (b + 2); |
|---|
| 1072 |
double denom = a * b * (a + b + 2) * (a + b + 3); |
|---|
| 1073 |
return 6 * numer / denom; |
|---|
| 1074 |
} |
|---|
| 1075 |
|
|---|
| 1076 |
void testBeta(double a, double b) { |
|---|
| 1077 |
double[] observ = new double[100_000]; |
|---|
| 1078 |
foreach(ref elem; observ) |
|---|
| 1079 |
elem = rBeta(a, b); |
|---|
| 1080 |
auto ksRes = ksTest(observ, paramBeta(a, b)); |
|---|
| 1081 |
auto summ = summary(observ); |
|---|
| 1082 |
writeln("100k samples from beta(", a, ", ", b, "): K-S P-val: ", ksRes.p); |
|---|
| 1083 |
writeln("\tMean Expected: ", a / (a + b), " Observed: ", summ.mean); |
|---|
| 1084 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 1085 |
writeln("\tStdev Expected: ", betaStdev(a, b), " Observed: ", summ.stdev); |
|---|
| 1086 |
writeln("\tKurtosis Expected: ", betaKurtosis(a, b), " Observed: ", summ.kurtosis); |
|---|
| 1087 |
writeln("\tSkewness Expected: ", betaSkew(a, b), " Observed: ", summ.skewness); |
|---|
| 1088 |
delete observ; |
|---|
| 1089 |
} |
|---|
| 1090 |
|
|---|
| 1091 |
testBeta(0.5, 0.7); |
|---|
| 1092 |
testBeta(5, 3); |
|---|
| 1093 |
} |
|---|
| 1094 |
|
|---|
| 1095 |
/// |
|---|
| 1096 |
double rLogistic(RGen = Random)(double loc, double scale, ref RGen gen = rndGen) { |
|---|
| 1097 |
dstatsEnforce(scale > 0, "scale must be > 0 for logistic distribution."); |
|---|
| 1098 |
|
|---|
| 1099 |
double U = uniform(0.0L, 1.0L, gen); |
|---|
| 1100 |
return loc + scale * log(U/(1.0 - U)); |
|---|
| 1101 |
} |
|---|
| 1102 |
|
|---|
| 1103 |
unittest { |
|---|
| 1104 |
double[] observ = new double[100_000]; |
|---|
| 1105 |
foreach(ref elem; observ) |
|---|
| 1106 |
elem = rLogistic(2.0L, 3.0L); |
|---|
| 1107 |
auto ksRes = ksTest(observ, parametrize!(logisticCDF)(2, 3)); |
|---|
| 1108 |
writeln("100k samples from logistic(2, 3): K-S P-val: ", ksRes.p); |
|---|
| 1109 |
writeln("\tMean Expected: 2 Observed: ", mean(observ)); |
|---|
| 1110 |
writeln("\tMedian Expected: 2 Observed: ", median(observ)); |
|---|
| 1111 |
writeln("\tStdev Expected: ", PI * PI * 3, " Observed: ", stdev(observ)); |
|---|
| 1112 |
writeln("\tKurtosis Expected: 1.2 Observed: ", kurtosis(observ)); |
|---|
| 1113 |
writeln("\tSkewness Expected: 0 Observed: ", skewness(observ)); |
|---|
| 1114 |
delete observ; |
|---|
| 1115 |
} |
|---|
| 1116 |
|
|---|
| 1117 |
/// |
|---|
| 1118 |
double rLogNorm(RGen = Random)(double mu, double sigma, ref RGen gen = rndGen) { |
|---|
| 1119 |
dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); |
|---|
| 1120 |
|
|---|
| 1121 |
return exp(rNorm(mu, sigma, gen)); |
|---|
| 1122 |
} |
|---|
| 1123 |
|
|---|
| 1124 |
unittest { |
|---|
| 1125 |
auto observ = randArray!rLogNorm(100_000, -2, 1); |
|---|
| 1126 |
auto ksRes = ksTest(observ, paramFunctor!(logNormalCDF)(-2, 1)); |
|---|
| 1127 |
|
|---|
| 1128 |
auto summ = summary(observ); |
|---|
| 1129 |
writeln("100k samples from log-normal(-2, 1): K-S P-val: ", ksRes.p); |
|---|
| 1130 |
writeln("\tMean Expected: ", exp(-1.5), " Observed: ", summ.mean); |
|---|
| 1131 |
writeln("\tMedian Expected: ", exp(-2.0L), " Observed: ", median(observ)); |
|---|
| 1132 |
writeln("\tStdev Expected: ", sqrt((exp(1.) - 1) * exp(-4.0L + 1)), |
|---|
| 1133 |
" Observed: ", summ.stdev); |
|---|
| 1134 |
writeln("\tKurtosis Expected: ?? Observed: ", summ.kurtosis); |
|---|
| 1135 |
writeln("\tSkewness Expected: ", (exp(1.) + 2) * sqrt(exp(1.) - 1), |
|---|
| 1136 |
" Observed: ", summ.skewness); |
|---|
| 1137 |
} |
|---|
| 1138 |
|
|---|
| 1139 |
/// |
|---|
| 1140 |
double rWeibull(RGen = Random)(double shape, double scale = 1, ref RGen gen = rndGen) { |
|---|
| 1141 |
dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution."); |
|---|
| 1142 |
dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution."); |
|---|
| 1143 |
|
|---|
| 1144 |
return pow(rExponential(1, gen), 1. / shape) * scale; |
|---|
| 1145 |
} |
|---|
| 1146 |
|
|---|
| 1147 |
unittest { |
|---|
| 1148 |
double[] observ = new double[100_000]; |
|---|
| 1149 |
foreach(ref elem; observ) |
|---|
| 1150 |
elem = rWeibull(2.0L, 3.0L); |
|---|
| 1151 |
auto ksRes = ksTest(observ, parametrize!(weibullCDF)(2.0, 3.0)); |
|---|
| 1152 |
writeln("100k samples from weibull(2, 3): K-S P-val: ", ksRes.p); |
|---|
| 1153 |
delete observ; |
|---|
| 1154 |
} |
|---|
| 1155 |
|
|---|
| 1156 |
/// |
|---|
| 1157 |
double rWald(RGen = Random)(double mu, double lambda, ref RGen gen = rndGen) { |
|---|
| 1158 |
dstatsEnforce(mu > 0, "mu must be > 0 for Wald distribution."); |
|---|
| 1159 |
dstatsEnforce(lambda > 0, "lambda must be > 0 for Wald distribution."); |
|---|
| 1160 |
|
|---|
| 1161 |
alias mu mean; |
|---|
| 1162 |
alias lambda scale; |
|---|
| 1163 |
|
|---|
| 1164 |
double mu_2l = mean / (2*scale); |
|---|
| 1165 |
double Y = rNorm(0, 1, gen); |
|---|
| 1166 |
Y = mean*Y*Y; |
|---|
| 1167 |
double X = mean + mu_2l*(Y - sqrt(4*scale*Y + Y*Y)); |
|---|
| 1168 |
double U = uniform(0.0L, 1.0L, gen); |
|---|
| 1169 |
if (U <= mean/(mean+X)) { |
|---|
| 1170 |
return X; |
|---|
| 1171 |
} else |
|---|
| 1172 |
|
|---|
| 1173 |
{ |
|---|
| 1174 |
return mean*mean/X; |
|---|
| 1175 |
} |
|---|
| 1176 |
} |
|---|
| 1177 |
|
|---|
| 1178 |
unittest { |
|---|
| 1179 |
auto observ = randArray!rWald(100_000, 4, 7); |
|---|
| 1180 |
auto ksRes = ksTest(observ, parametrize!(waldCDF)(4, 7)); |
|---|
| 1181 |
|
|---|
| 1182 |
auto summ = summary(observ); |
|---|
| 1183 |
writeln("100k samples from wald(4, 7): K-S P-val: ", ksRes.p); |
|---|
| 1184 |
writeln("\tMean Expected: ", 4, " Observed: ", summ.mean); |
|---|
| 1185 |
writeln("\tMedian Expected: ?? Observed: ", median(observ)); |
|---|
| 1186 |
writeln("\tStdev Expected: ", sqrt(64.0 / 7), " Observed: ", summ.stdev); |
|---|
| 1187 |
writeln("\tKurtosis Expected: ", 15.0 * 4 / 7, " Observed: ", summ.kurtosis); |
|---|
| 1188 |
writeln("\tSkewness Expected: ", 3 * sqrt(4.0 / 7), " Observed: ", summ.skewness); |
|---|
| 1189 |
} |
|---|
| 1190 |
|
|---|
| 1191 |
/// |
|---|
| 1192 |
double rRayleigh(RGen = Random)(double mode, ref RGen gen = rndGen) { |
|---|
| 1193 |
dstatsEnforce(mode > 0, "mode must be > 0 for Rayleigh distribution."); |
|---|
| 1194 |
|
|---|
| 1195 |
return mode*sqrt(-2.0 * log(1.0 - uniform(0.0L, 1.0L, gen))); |
|---|
| 1196 |
} |
|---|
| 1197 |
|
|---|
| 1198 |
unittest { |
|---|
| 1199 |
auto observ = randArray!rRayleigh(100_000, 3); |
|---|
| 1200 |
auto ksRes = ksTest(observ, parametrize!(rayleighCDF)(3)); |
|---|
| 1201 |
writeln("100k samples from rayleigh(3): K-S P-val: ", ksRes.p); |
|---|
| 1202 |
} |
|---|