/// <summary> /// Efficiently computes a sorted random set of <tt>count</tt> elements from the interval <tt>[low,low+N-1]</tt>. /// Since we are talking about a random set, no element will occur more than once. /// /// <p>Running time is <tt>O(count)</tt>, on average. Space requirements are zero. /// /// <p>Numbers are filled into the specified array starting at index <tt>fromIndex</tt> to the right. /// The array is returned sorted ascending in the range filled with numbers. /// /// <p><b>Random number generation:</b> By default uses <tt>MersenneTwister</tt>, a very strong random number generator, much better than <tt>java.util.Random</tt>. /// You can also use other strong random number generators of Paul Houle's RngPack package. /// For example, <tt>Ranecu</tt>, <tt>Ranmar</tt> and <tt>Ranlux</tt> are strong well analyzed research grade pseudo-random number generators with known periods. /// </summary> /// <param name="n">the total number of elements to choose (must be <tt>n >= 0</tt> and <tt>n <= N</tt>).</param> /// <param name="N">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>.</param> /// <param name="count">the number of elements to be filled into <tt>values</tt> by this call (must be >= 0 and <=<tt>n</tt>). Normally, you will set <tt>count=n</tt>.</param> /// <param name="low">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>. Hint: If <tt>low==0</tt>, then draws random numbers from the interval <tt>[0,N-1]</tt>.</param> /// <param name="values">the array into which the random numbers are to be filled; must have a length <tt>>= count+fromIndex</tt>.</param> /// <param name="fromIndex">the first index within <tt>values</tt> to be filled with numbers (inclusive).</param> /// <param name="randomGenerator">a random number generator. Set this parameter to <tt>null</tt> to use the default random number generator.</param> protected static void SampleMethodA(long n, long N, int count, long low, long[] values, int fromIndex, RandomEngine randomGenerator) { double V, quot, Nreal, top; long S; long chosen = -1 + low; top = N - n; Nreal = N; while (n >= 2 && count > 0) { V = randomGenerator.Raw(); S = 0; quot = top / Nreal; while (quot > V) { S++; top--; Nreal--; quot = (quot * top) / Nreal; } chosen += S + 1; values[fromIndex++] = chosen; count--; Nreal--; n--; } if (count > 0) { // special case n==1 S = (long)(System.Math.Round(Nreal) * randomGenerator.Raw()); chosen += S + 1; values[fromIndex] = chosen; } }
/// <summary> /// Returns a random number from the distribution; bypasses the internal state. /// </summary> /// <param name="theMean"></param> /// <returns></returns> private int NextInt(double theMean) { /* * Adapted from "Numerical Recipes in C". */ double xm = theMean; double g = this.cached_g; if (xm == -1.0) { return(0); // not defined } if (xm < SWITCH_MEAN) { int poisson = -1; double product = 1; do { poisson++; product *= RandomGenerator.Raw(); } while (product >= g); // bug in CLHEP 1.4.0: was "} while ( product > g );" return(poisson); } else if (xm < MEAN_MAX) { double t; double em; double sq = this.cached_sq; double alxm = this.cached_alxm; RandomEngine rand = this.RandomGenerator; do { double y; do { y = System.Math.Tan(System.Math.PI * rand.Raw()); em = sq * y + xm; } while (em < 0.0); em = (double)(int)(em); // faster than em = System.Math.Floor(em); (em>=0.0) t = 0.9 * (1.0 + y * y) * System.Math.Exp(em * alxm - LogGamma(em + 1.0) - g); } while (rand.Raw() > t); return((int)em); } else { // mean is too large return((int)xm); } }
/// <summary> /// Returns a random number from the standard Triangular distribution in (-1,1). /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>tra.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// <p> /// </summary> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextTriangular(RandomEngine randomGenerator) { /****************************************************************** * * * Triangular Distribution - Inversion: x = +-(1-sqrt(u)) * * * ****************************************************************** * * * FUNCTION : - tra samples a random number from the * * standard Triangular distribution in (-1,1) * * SUBPROGRAM : - drand(seed) ..d (0,1)-Uniform generator with * * unsigned long int *seedd * * * ******************************************************************/ double u; u = randomGenerator.Raw(); if (u <= 0.5) { return(System.Math.Sqrt(2.0 * u) - 1.0); /* -1 <= x <= 0 */ } else { return(1.0 - System.Math.Sqrt(2.0 * (1.0 - u))); /* 0 <= x <= 1 */ } }
/// <summary> /// Returns a discrete geometric distributed random number; <A HREF="http://www.statsoft.com/textbook/glosf.html#Geometric Distribution">Definition</A>. /// <p> /// <i>p(k) = p * (1-p)^k</i> for <i> k >= 0</i>. /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>geo.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// <p> /// </summary> /// <param name="p">must satisfy <i>0 < p < 1</i>.</param> /// <param name="randomGenerator"></param> /// <returns></returns> public static int NextGeometric(double p, RandomEngine randomGenerator) { /****************************************************************** * * * Geometric Distribution - Inversion * * * ****************************************************************** * * * On generating random numbers of a discrete distribution by * * Inversion normally sequential search is necessary, but in the * * case of the Geometric distribution a direct transformation is * * possible because of the special parallel to the continuous * * Exponential distribution Exp(t): * * X - Exp(t): G(x)=1-exp(-tx) * * Geo(p): pk=G(k+1)-G(k)=exp(-tk)*(1-exp(-t)) * * p=1-exp(-t) * * A random number of the Geometric distribution Geo(p) is * * obtained by k=(long int)x, where x is from Exp(t) with * * parameter t=-log(1-p)d * * * ****************************************************************** * * * FUNCTION: - geo samples a random number from the Geometric * * distribution with parameter 0<p<1d * * SUBPROGRAMS: - drand(seed) ..d (0,1)-Uniform generator with * * unsigned long int *seedd * * * ******************************************************************/ double u = randomGenerator.Raw(); return((int)(System.Math.Log(u) / System.Math.Log(1.0 - p))); }
/// <summary> /// Returns a random number from the Burr III, IV, V, VI, IX, XII distributions. /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>burr2.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// C-RAND's implementation, in turn, is based upon /// <p> /// Ld Devroye (1986): Non-Uniform Random Variate Generation, Springer Verlag, New Yorkd /// <p> /// </summary> /// <param name="r">must be > 0.</param> /// <param name="k">must be > 0.</param> /// <param name="nr">the number of the burr distribution (e.gd 3,4,5,6,9,12).</param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextBurr2(double r, double k, int nr, RandomEngine randomGenerator) { /****************************************************************** * * * Burr III, IV, V, VI, IX, XII Distribution - Inversion * * * ****************************************************************** * * * FUNCTION : - burr2 samples a random number from one of the * * Burr III, IV, V, VI, IX, XII distributions with * * parameters r > 0 and k > 0, where the nod of * * the distribution is indicated by a pointer * * variabled * * REFERENCE : - Ld Devroye (1986): Non-Uniform Random Variate * * Generation, Springer Verlag, New Yorkd * * SUBPROGRAM : - drand(seed) ..d (0,1)-Uniform generator with * * unsigned long int *seedd * * * ******************************************************************/ double y, u; u = randomGenerator.Raw(); // U(0/1) y = System.Math.Exp(-System.Math.Log(u) / r) - 1.0; // u^(-1/r) - 1 switch (nr) { case 3: // BURR III return(System.Math.Exp(-System.Math.Log(y) / k)); // y^(-1/k) case 4: // BURR IV y = System.Math.Exp(k * System.Math.Log(y)) + 1.0; // y^k + 1 y = k / y; return(y); case 5: // BURR V y = System.Math.Atan(-System.Math.Log(y / k)); // arctan[log(y/k)] return(y); case 6: // BURR VI y = -System.Math.Log(y / k) / r; y = System.Math.Log(y + System.Math.Sqrt(y * y + 1.0)); return(y); case 9: // BURR IX y = 1.0 + 2.0 * u / (k * (1.0 - u)); y = System.Math.Exp(System.Math.Log(y) / r) - 1.0; // y^(1/r) -1 return(System.Math.Log(y)); case 12: // BURR XII return(System.Math.Exp(System.Math.Log(y) / k)); // y^(1/k) } return(0); }
/// <summary> /// Returns a Laplace (Double Exponential) distributed random number from the standard Laplace distribution L(0,1)d /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>lapin.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// <p> /// </summary> /// <param name="randomGenerator"></param> /// <returns>a number in the open unit interval <code>(0.0,1.0)</code> (excluding 0.0 and 1.0).</returns> public static double NextLaplace(RandomEngine randomGenerator) { double u = randomGenerator.Raw(); u = u + u - 1.0; if (u > 0) { return(-System.Math.Log(1.0 - u)); } else { return(System.Math.Log(1.0 + u)); } }
/// <summary> /// Returns a zipfian distributed random number with the given skew. /// <p> /// Algorithm from page 551 of: /// Devroye, Luc (1986) `Non-uniform random variate generation', /// Springer-Verlag: Berlind ISBN 3-540-96305-7 (also 0-387-96305-7) /// </summary> /// <param name="z">the skew of the distribution (must be >1.0).</param> /// <param name="randomGenerator"></param> /// <returns>a zipfian distributed number in the closed interval <i>[1,int.MaxValue]</i>.</returns> public static int NextZipfInt(double z, RandomEngine randomGenerator) { /* Algorithm from page 551 of: * Devroye, Luc (1986) `Non-uniform random variate generation', * Springer-Verlag: Berlind ISBN 3-540-96305-7 (also 0-387-96305-7) */ double b = System.Math.Pow(2.0, z - 1.0); double constant = -1.0 / (z - 1.0); int result = 0; for (; ;) { double u = randomGenerator.Raw(); double v = randomGenerator.Raw(); result = (int)(System.Math.Floor(System.Math.Pow(u, constant))); double t = System.Math.Pow(1.0 + 1.0 / result, z - 1.0); if (v * result * (t - 1.0) / (b - 1.0) <= t / b) { break; } } return(result); }
/// <summary> /// Returns an erlang distributed random number with the given variance and mean. /// </summary> /// <param name="variance"></param> /// <param name="mean"></param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextErlang(double variance, double mean, RandomEngine randomGenerator) { int k = (int)((mean * mean) / variance + 0.5); k = (k > 0) ? k : 1; double a = k / mean; double prod = 1.0; for (int i = 0; i < k; i++) { prod *= randomGenerator.Raw(); } return(-System.Math.Log(prod) / a); }
/// <summary> /// Returns a lambda distributed random number with parameters l3 and l4. /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>lamin.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// C-RAND's implementation, in turn, is based upon /// <p> /// J.Sd Ramberg, B:Wd Schmeiser (1974): An approximate method for generating asymmetric variables, Communications ACM 17, 78-82. /// <p> /// </summary> /// <param name="l3"></param> /// <param name="l4"></param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextLambda(double l3, double l4, RandomEngine randomGenerator) { double l_sign; if ((l3 < 0) || (l4 < 0)) { l_sign = -1.0; // sign(l) } else { l_sign = 1.0; } double u = randomGenerator.Raw(); // U(0/1) double x = l_sign * (System.Math.Exp(System.Math.Log(u) * l3) - System.Math.Exp(System.Math.Log(1.0 - u) * l4)); return(x); }
/// <summary> /// Returns a random number from the Burr II, VII, VIII, X Distributions. /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>burr1.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// C-RAND's implementation, in turn, is based upon /// <p> /// Ld Devroye (1986): Non-Uniform Random Variate Generation, Springer Verlag, New Yorkd /// <p> /// </summary> /// <param name="r">must be > 0.</param> /// <param name="nr">the number of the burr distribution (e.gd 2,7,8,10).</param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextBurr1(double r, int nr, RandomEngine randomGenerator) { /****************************************************************** * * * Burr II, VII, VIII, X Distributions - Inversion * * * ****************************************************************** * * * FUNCTION : - burr1 samples a random number from one of the * * Burr II, VII, VIII, X distributions with * * parameter r > 0 , where the nod of the * * distribution is indicated by a pointer * * variabled * * REFERENCE : - Ld Devroye (1986): Non-Uniform Random Variate * * Generation, Springer Verlag, New Yorkd * * SUBPROGRAM : - drand(seed) ..d (0,1)-uniform generator with * * unsigned long int *seedd * * * ******************************************************************/ double y; y = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) / r); /* y=u^(1/r) */ switch (nr) { // BURR II case 2: return(-System.Math.Log(1 / y - 1)); // BURR VII case 7: return(System.Math.Log(2 * y / (2 - 2 * y)) / 2); // BURR VIII case 8: return(System.Math.Log(System.Math.Tan(y * System.Math.PI / 2.0))); // BURR X case 10: return(System.Math.Sqrt(-System.Math.Log(1 - y))); } return(0); }
/// <summary> /// Returns a power-law distributed random number with the given exponent and lower cutoff. /// </summary> /// <param name="alpha">the exponent </param> /// <param name="cut">the lower cutoff</param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextPowLaw(double alpha, double cut, RandomEngine randomGenerator) { return(cut * System.Math.Pow(randomGenerator.Raw(), 1.0 / (alpha + 1.0))); }
/// <summary> /// Computes a sorted random set of <tt>count</tt> elements from the interval <tt>[low,low+N-1]</tt>. /// Since we are talking about a random set, no element will occur more than once. /// /// <p>Running time is <tt>O(N)</tt>, on average. Space requirements are zero. /// /// <p>Numbers are filled into the specified array starting at index <tt>fromIndex</tt> to the right. /// The array is returned sorted ascending in the range filled with numbers. /// </summary> /// <param name="n">the total number of elements to choose (must be >= 0).</param> /// <param name="N">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>.</param> /// <param name="count">the number of elements to be filled into <tt>values</tt> by this call (must be >= 0 and <=<tt>n</tt>). Normally, you will set <tt>count=n</tt>.</param> /// <param name="low">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>. Hint: If <tt>low==0</tt>, then draws random numbers from the interval <tt>[0,N-1]</tt>.</param> /// <param name="values">the array into which the random numbers are to be filled; must have a length <tt>>= count+fromIndex</tt>.</param> /// <param name="fromIndex">the first index within <tt>values</tt> to be filled with numbers (inclusive).</param> /// <param name="randomGenerator">a random number generator.</param> protected static void SampleMethodD(long n, long N, int count, long low, long[] values, int fromIndex, RandomEngine randomGenerator) { double nreal, Nreal, ninv, nmin1inv, U, X, Vprime, y1, y2, top, bottom, negSreal, qu1real; long qu1, threshold, t, limit; long S; long chosen = -1 + low; long negalphainv = -13; //tuning paramter, determines when to switch from method D to method Ad Dependent on programming language, platform, etc. nreal = n; ninv = 1.0 / nreal; Nreal = N; Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); qu1 = -n + 1 + N; qu1real = -nreal + 1.0 + Nreal; threshold = -negalphainv * n; while (n > 1 && count > 0 && threshold < N) { nmin1inv = 1.0 / (-1.0 + nreal); for (;;) { for (;;) { // step D2: generate U and X X = Nreal * (-Vprime + 1.0); S = (long)X; if (S < qu1) { break; } Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); } U = randomGenerator.Raw(); negSreal = -S; //step D3: Accept? y1 = System.Math.Exp(System.Math.Log(U * Nreal / qu1real) * nmin1inv); Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real)); if (Vprime <= 1.0) { break; //break inner loop } //step D4: Accept? y2 = 1.0; top = -1.0 + Nreal; if (n - 1 > S) { bottom = -nreal + Nreal; limit = -S + N; } else { bottom = -1.0 + negSreal + Nreal; limit = qu1; } for (t = N - 1; t >= limit; t--) { y2 = (y2 * top) / bottom; top--; bottom--; } if (Nreal / (-X + Nreal) >= y1 * System.Math.Exp(System.Math.Log(y2) * nmin1inv)) { // accept ! Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * nmin1inv); break; //break inner loop } Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); } //end for //step D5: select the (S+1)st record ! chosen += S + 1; values[fromIndex++] = chosen; /* * // invert * for (int iter=0; iter<S && count > 0; iter++) { * values[fromIndex++] = ++chosen; * count--; * } * chosen++; */ count--; N -= S + 1; Nreal = negSreal + (-1.0 + Nreal); n--; nreal--; ninv = nmin1inv; qu1 = -S + qu1; qu1real = negSreal + qu1real; threshold += negalphainv; } //end while if (count > 0) { if (n > 1) { //faster to use method A to finish the sampling SampleMethodA(n, N, count, chosen + 1, values, fromIndex, randomGenerator); } else { //special case n==1 S = (long)(N * Vprime); chosen += S + 1; values[fromIndex++] = chosen; } } }
/// <summary> /// Efficiently computes a sorted random set of <tt>count</tt> elements from the interval <tt>[low,low+N-1]</tt>. /// Since we are talking about a random set, no element will occur more than once. /// /// <p>Running time is <tt>O(count)</tt>, on average. Space requirements are zero. /// /// <p>Numbers are filled into the specified array starting at index <tt>fromIndex</tt> to the right. /// The array is returned sorted ascending in the range filled with numbers. /// </summary> /// <param name="n">the total number of elements to choose (must be >= 0).</param> /// <param name="N">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>.</param> /// <param name="count">the number of elements to be filled into <tt>values</tt> by this call (must be >= 0 and <=<tt>n</tt>). Normally, you will set <tt>count=n</tt>.</param> /// <param name="low">the interval to choose random numbers from is <tt>[low,low+N-1]</tt>. Hint: If <tt>low==0</tt>, then draws random numbers from the interval <tt>[0,N-1]</tt>.</param> /// <param name="values">the array into which the random numbers are to be filled; must have a length <tt>>= count+fromIndex</tt>.</param> /// <param name="fromIndex">the first index within <tt>values</tt> to be filled with numbers (inclusive).</param> /// <param name="randomGenerator">a random number generator.</param> protected static void RejectMethodD(long n, long N, int count, long low, long[] values, int fromIndex, RandomEngine randomGenerator) { /* This algorithm is applicable if a large percentage (90%..100%) of N shall be sampled. * In such cases it is more efficient than sampleMethodA() and sampleMethodD(). * The idea is that it is more efficient to express * sample(n,N,count) in terms of reject(N-n,N,count) * and then invert the result. * For example, sampling 99% turns into sampling 1% plus inversion. * * This algorithm is the same as method sampleMethodD(..d) with the exception that sampled elements are rejected, and not sampled elements included in the result set. */ n = N - n; // IMPORTANT !!! double nreal, Nreal, ninv, nmin1inv, U, X, Vprime, y1, y2, top, bottom, negSreal, qu1real; long qu1, t, limit; //long threshold; long S; long chosen = -1 + low; //long negalphainv = -13; //tuning paramter, determines when to switch from method D to method Ad Dependent on programming language, platform, etc. nreal = n; ninv = 1.0 / nreal; Nreal = N; Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); qu1 = -n + 1 + N; qu1real = -nreal + 1.0 + Nreal; //threshold = -negalphainv * n; while (n > 1 && count > 0) { //&& threshold<N) { nmin1inv = 1.0 / (-1.0 + nreal); for (;;) { for (;;) { // step D2: generate U and X X = Nreal * (-Vprime + 1.0); S = (long)X; if (S < qu1) { break; } Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); } U = randomGenerator.Raw(); negSreal = -S; //step D3: Accept? y1 = System.Math.Exp(System.Math.Log(U * Nreal / qu1real) * nmin1inv); Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real)); if (Vprime <= 1.0) { break; //break inner loop } //step D4: Accept? y2 = 1.0; top = -1.0 + Nreal; if (n - 1 > S) { bottom = -nreal + Nreal; limit = -S + N; } else { bottom = -1.0 + negSreal + Nreal; limit = qu1; } for (t = N - 1; t >= limit; t--) { y2 = (y2 * top) / bottom; top--; bottom--; } if (Nreal / (-X + Nreal) >= y1 * System.Math.Exp(System.Math.Log(y2) * nmin1inv)) { // accept ! Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * nmin1inv); break; //break inner loop } Vprime = System.Math.Exp(System.Math.Log(randomGenerator.Raw()) * ninv); } //end for //step D5: reject the (S+1)st record ! int iter = count; //int iter = (int)(System.Math.Min(S,count)); if (S < iter) { iter = (int)S; } count -= iter; for (; --iter >= 0;) { values[fromIndex++] = ++chosen; } chosen++; N -= S + 1; Nreal = negSreal + (-1.0 + Nreal); n--; nreal--; ninv = nmin1inv; qu1 = -S + qu1; qu1real = negSreal + qu1real; //threshold += negalphainv; } //end while if (count > 0) { //special case n==1 //reject the (S+1)st record ! S = (long)(N * Vprime); int iter = count; //int iter = (int)(System.Math.Min(S,count)); if (S < iter) { iter = (int)S; } count -= iter; for (; --iter >= 0;) { values[fromIndex++] = ++chosen; } chosen++; // fill the rest for (; --count >= 0;) { values[fromIndex++] = ++chosen; } } }
/// <summary> /// Returns a weibull distributed random numberd /// Polar method. /// See Simulation, Modelling & Analysis by Law & Kelton, pp259 /// </summary> /// <param name="alpha"></param> /// <param name="beta"></param> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextWeibull(double alpha, double beta, RandomEngine randomGenerator) { // Polar method. // See Simulation, Modelling & Analysis by Law & Kelton, pp259 return(System.Math.Pow(beta * (-System.Math.Log(1.0 - randomGenerator.Raw())), 1.0 / alpha)); }
/// <summary> /// Returns a random number from the distribution. /// </summary> /// <param name="N"></param> /// <param name="M"></param> /// <param name="n"></param> /// <param name="randomGenerator"></param> /// <returns></returns> protected int Hmdu(int N, int M, int n, RandomEngine randomGenerator) { int I, K; double p, nu, c, d, U; if (N != N_last || M != M_last || n != n_last) { // set-up */ N_last = N; M_last = M; n_last = n; Mp = (double)(M + 1); np = (double)(n + 1); N_Mn = N - M - n; p = Mp / (N + 2.0); nu = np * p; /* mode, real */ if ((m = (int)nu) == nu && p == 0.5) { /* mode, int */ mp = m--; } else { mp = m + 1; /* mp = m + 1 */ } /* mode probability, using the external function flogfak(k) = ln(k!) */ fm = System.Math.Exp(Arithmetic.LogFactorial(N - M) - Arithmetic.LogFactorial(N_Mn + m) - Arithmetic.LogFactorial(n - m) + Arithmetic.LogFactorial(M) - Arithmetic.LogFactorial(M - m) - Arithmetic.LogFactorial(m) - Arithmetic.LogFactorial(N) + Arithmetic.LogFactorial(N - n) + Arithmetic.LogFactorial(n)); /* safety bound - guarantees at least 17 significant decimal digits */ /* b = min(n, (long int)(nu + k*c')) */ b = (int)(nu + 11.0 * System.Math.Sqrt(nu * (1.0 - p) * (1.0 - n / (double)N) + 1.0)); if (b > n) { b = n; } } for (; ;) { if ((U = randomGenerator.Raw() - fm) <= 0.0) { return(m); } c = d = fm; /* down- and upward search from the mode */ for (I = 1; I <= m; I++) { K = mp - I; /* downward search */ c *= (double)K / (np - K) * ((double)(N_Mn + K) / (Mp - K)); if ((U -= c) <= 0.0) { return(K - 1); } K = m + I; /* upward search */ d *= (np - K) / (double)K * ((Mp - K) / (double)(N_Mn + K)); if ((U -= d) <= 0.0) { return(K); } } /* upward search from K = 2m + 1 to K = b */ for (K = mp + m; K <= b; K++) { d *= (np - K) / (double)K * ((Mp - K) / (double)(N_Mn + K)); if ((U -= d) <= 0.0) { return(K); } } } }
/// <summary> /// Returns a zeta distributed random number. /// </summary> /// <param name="ro"></param> /// <param name="pk"></param> /// <param name="randomGenerator"></param> /// <returns></returns> protected long GenerateZeta(double ro, double pk, RandomEngine randomGenerator) { /****************************************************************** * * * Zeta Distribution - Acceptance Rejection * * * ****************************************************************** * * * To sample from the Zeta distribution with parameters ro and pk * * it suffices to sample variates x from the distribution with * * density function f(x)=B*{[x+0.5]+pk}^(-(1+ro))( x > .5 ) * * and then deliver k=[x+0.5]d * * 1/B=Sum[(j+pk)^-(ro+1)] (j=1,2,..d) converges for ro >= .5 d * * It is not necessary to compute B, because variates x are * * generated by acceptance rejection using density function * * g(x)=ro*(c+0.5)^ro*(c+x)^-(ro+1)d * * * * * int overflow is possible, when ro is small (ro <= .5) and * * pk larged In this case a new sample is generatedd If ro and pk * * satisfy the inequality ro > .14 + pk*1.85e-8 + .02*ln(pk) * * the percentage of overflow is less than 1%, so that the * * result is reliabled * * NOTE: The comment above is likely to be nomore valid since * * the C-version operated on 32-bit ints, while this Java * * version operates on 64-bit intsd However, the following is * * still valid: * * * * * * If either ro > 100 or k > 10000 numerical problems in * * computing the theoretical moments arise, therefore ro<=100 and * * k<=10000 are recommendedd * * * ****************************************************************** * * * FUNCTION: - zeta samples a random number from the * * Zeta distribution with parameters ro > 0 and * * pk >= 0d * * REFERENCE: - Jd Dagpunar (1988): Principles of Random * * Variate Generation, Clarendon Press, Oxfordd * * * ******************************************************************/ double u, v, e, x; long k; if (ro != ro_prev || pk != pk_prev) { // Set-up ro_prev = ro; pk_prev = pk; if (ro < pk) { c = pk - 0.5; d = 0; } else { c = ro - 0.5; d = (1.0 + ro) * System.Math.Log((1.0 + pk) / (1.0 + ro)); } } do { do { u = randomGenerator.Raw(); v = randomGenerator.Raw(); x = (c + 0.5) * System.Math.Exp(-System.Math.Log(u) / ro) - c; } while (x <= 0.5 || x >= maxlongint); k = (int)(x + 0.5); e = -System.Math.Log(v); } while (e < (1.0 + ro) * System.Math.Log((k + pk) / (x + c)) - d); return(k); }
/// <summary> /// Returns a random number from the standard Logistic distribution Log(0,1). /// <p> /// <b>Implementation:</b> Inversion method. /// This is a port of <i>login.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// </summary> /// <param name="randomGenerator"></param> /// <returns></returns> public static double NextLogistic(RandomEngine randomGenerator) { double u = randomGenerator.Raw(); return(-System.Math.Log(1.0 / u - 1.0)); }
/// <summary> /// Returns a cauchy distributed random number from the standard Cauchy distribution C(0,1)d /// <A HREF="http://www.cern.ch/RD11/rkb/AN16pp/node25.html#SECTION000250000000000000000"> math definition</A> /// and <A HREF="http://www.statsoft.com/textbook/glosc.html#Cauchy Distribution"> animated definition</A>d /// <p> /// <i>p(x) = 1/ (mean*pi * (1+(x/mean)^2))</i>. /// <p> /// <b>Implementation:</b> /// This is a port of <i>cin.c</i> from the <A HREF="http://www.cis.tu-graz.ac.at/stat/stadl/random.html">C-RAND / WIN-RAND</A> library. /// <p> /// </summary> /// <param name="randomGenerator"></param> /// <returns>a number in the open unit interval <code>(0.0,1.0)</code> (excluding 0.0 and 1.0).</returns> public static double NextCauchy(RandomEngine randomGenerator) { return(System.Math.Tan(System.Math.PI * randomGenerator.Raw())); }
/// <summary> /// Returns a random number from the distribution; bypasses the internal state. /// </summary> /// <param name="theMean"></param> /// <returns></returns> public int NextInt(double theMean) { /****************************************************************** * * * Poisson Distribution - Patchwork Rejection/Inversion * * * ****************************************************************** * * * For parameter my < 10 Tabulated Inversion is appliedd * * For my >= 10 Patchwork Rejection is employed: * * The area below the histogram function f(x) is rearranged in * * its body by certain point reflectionsd Within a large center * * interval variates are sampled efficiently by rejection from * * uniform hatsd Rectangular immediate acceptance regions speed * * up the generationd The remaining tails are covered by * * exponential functionsd * * * *****************************************************************/ RandomEngine gen = this.RandomGenerator; double my = theMean; double t, g, my_k; double gx, gy, px, py, e, x, xx, delta, v; int sign; //static double p,q,p0,pp[36]; //static long ll,m; double u; int k, i; if (my < SWITCH_MEAN) { // CASE B: Inversion- start new table and calculate p0 if (my != my_old) { my_old = my; llll = 0; p = System.Math.Exp(-my); q = p; p0 = p; //for (k=pp.Length; --k >=0; ) pp[k] = 0; } m = (my > 1.0) ? (int)my : 1; for (; ;) { u = gen.Raw(); // Step Ud Uniform sample k = 0; if (u <= p0) { return(k); } if (llll != 0) { // Step Td Table comparison i = (u > 0.458) ? System.Math.Min(llll, m) : 1; for (k = i; k <= llll; k++) { if (u <= pp[k]) { return(k); } } if (llll == 35) { continue; } } for (k = llll + 1; k <= 35; k++) { // Step Cd Creation of new probd p *= my / (double)k; q += p; pp[k] = q; if (u <= q) { llll = k; return(k); } } llll = 35; } } // end my < SWITCH_MEAN else if (my < MEAN_MAX) { // CASE A: acceptance complement //static double my_last = -1.0; //static long int m, k2, k4, k1, k5; //static double dl, dr, r1, r2, r4, r5, ll, lr, l_my, c_pm, // f1, f2, f4, f5, p1, p2, p3, p4, p5, p6; int Dk, X, Y; double Ds, U, V, W; m = (int)my; if (my != my_last) { // set-up my_last = my; // approximate deviation of reflection points k2, k4 from my - 1/2 Ds = System.Math.Sqrt(my + 0.25); // mode m, reflection points k2 and k4, and points k1 and k5, which // delimit the centre region of h(x) k2 = (int)System.Math.Ceiling(my - 0.5 - Ds); k4 = (int)(my - 0.5 + Ds); k1 = k2 + k2 - m + 1; k5 = k4 + k4 - m; // range width of the critical left and right centre region dl = (double)(k2 - k1); dr = (double)(k5 - k4); // recurrence constants r(k) = p(k)/p(k-1) at k = k1, k2, k4+1, k5+1 r1 = my / (double)k1; r2 = my / (double)k2; r4 = my / (double)(k4 + 1); r5 = my / (double)(k5 + 1); // reciprocal values of the scale parameters of expond tail envelopes ll = System.Math.Log(r1); // expond tail left lr = -System.Math.Log(r5); // expond tail right // Poisson constants, necessary for computing function values f(k) l_my = System.Math.Log(my); c_pm = m * l_my - Arithmetic.LogFactorial(m); // function values f(k) = p(k)/p(m) at k = k2, k4, k1, k5 f2 = Function(k2, l_my, c_pm); f4 = Function(k4, l_my, c_pm); f1 = Function(k1, l_my, c_pm); f5 = Function(k5, l_my, c_pm); // area of the two centre and the two exponential tail regions // area of the two immediate acceptance regions between k2, k4 p1 = f2 * (dl + 1.0); // immedd left p2 = f2 * dl + p1; // centre left p3 = f4 * (dr + 1.0) + p2; // immedd right p4 = f4 * dr + p3; // centre right p5 = f1 / ll + p4; // expond tail left p6 = f5 / lr + p5; // expond tail right } // end set-up for (; ;) { // generate uniform number U -- U(0, p6) // case distinction corresponding to U if ((U = gen.Raw() * p6) < p2) { // centre left // immediate acceptance region R2 = [k2, m) *[0, f2), X = k2, ..d m -1 if ((V = U - p1) < 0.0) { return(k2 + (int)(U / f2)); } // immediate acceptance region R1 = [k1, k2)*[0, f1), X = k1, ..d k2-1 if ((W = V / dl) < f1) { return(k1 + (int)(V / f1)); } // computation of candidate X < k2, and its counterpart Y > k2 // either squeeze-acceptance of X or acceptance-rejection of Y Dk = (int)(dl * gen.Raw()) + 1; if (W <= f2 - Dk * (f2 - f2 / r2)) { // quick accept of return(k2 - Dk); // X = k2 - Dk } if ((V = f2 + f2 - W) < 1.0) { // quick reject of Y Y = k2 + Dk; if (V <= f2 + Dk * (1.0 - f2) / (dl + 1.0)) { // quick accept of return(Y); // Y = k2 + Dk } if (V <= Function(Y, l_my, c_pm)) { return(Y); // accept of Y } } X = k2 - Dk; } else if (U < p4) { // centre right // immediate acceptance region R3 = [m, k4+1)*[0, f4), X = m, ..d k4 if ((V = U - p3) < 0.0) { return(k4 - (int)((U - p2) / f4)); } // immediate acceptance region R4 = [k4+1, k5+1)*[0, f5) if ((W = V / dr) < f5) { return(k5 - (int)(V / f5)); } // computation of candidate X > k4, and its counterpart Y < k4 // either squeeze-acceptance of X or acceptance-rejection of Y Dk = (int)(dr * gen.Raw()) + 1; if (W <= f4 - Dk * (f4 - f4 * r4)) { // quick accept of return(k4 + Dk); // X = k4 + Dk } if ((V = f4 + f4 - W) < 1.0) { // quick reject of Y Y = k4 - Dk; if (V <= f4 + Dk * (1.0 - f4) / dr) { // quick accept of return(Y); // Y = k4 - Dk } if (V <= Function(Y, l_my, c_pm)) { return(Y); // accept of Y } } X = k4 + Dk; } else { W = gen.Raw(); if (U < p5) { // expond tail left Dk = (int)(1.0 - System.Math.Log(W) / ll); if ((X = k1 - Dk) < 0) { continue; // 0 <= X <= k1 - 1 } W *= (U - p4) * ll; // W -- U(0, h(x)) if (W <= f1 - Dk * (f1 - f1 / r1)) { return(X); // quick accept of X } } else { // expond tail right Dk = (int)(1.0 - System.Math.Log(W) / lr); X = k5 + Dk; // X >= k5 + 1 W *= (U - p5) * lr; // W -- U(0, h(x)) if (W <= f5 - Dk * (f5 - f5 * r5)) { return(X); // quick accept of X } } } // acceptance-rejection test of candidate X from the original area // test, whether W <= f(k), with W = U*h(x) and U -- U(0, 1) // log f(X) = (X - m)*log(my) - log X! + log m! if (System.Math.Log(W) <= X * l_my - Arithmetic.LogFactorial(X) - c_pm) { return(X); } } } else { // mean is too large return((int)my); } }
/// <summary> /// Returns a random number from the distribution. /// Uses the Patchwork Rejection method of Heinz Zechner (HPRS) /// </summary> /// <param name="N"></param> /// <param name="M"></param> /// <param name="n"></param> /// <param name="randomGenerator"></param> /// <returns></returns> protected int Hprs(int N, int M, int n, RandomEngine randomGenerator) { int Dk, X, V; double Mp, np, p, nu, U, Y, W; /* (X, Y) <-> (V, W) */ if (N != N_last || M != M_last || n != n_last) { /* set-up */ N_last = N; M_last = M; n_last = n; Mp = (double)(M + 1); np = (double)(n + 1); N_Mn = N - M - n; p = Mp / (N + 2.0); nu = np * p; // main parameters // approximate deviation of reflection points k2, k4 from nu - 1/2 U = System.Math.Sqrt(nu * (1.0 - p) * (1.0 - (n + 2.0) / (N + 3.0)) + 0.25); // mode m, reflection points k2 and k4, and points k1 and k5, which // delimit the centre region of h(x) // k2 = ceil (nu - 1/2 - U), k1 = 2*k2 - (m - 1 + delta_ml) // k4 = floor(nu - 1/2 + U), k5 = 2*k4 - (m + 1 - delta_mr) m = (int)nu; k2 = (int)System.Math.Ceiling(nu - 0.5 - U); if (k2 >= m) { k2 = m - 1; } k4 = (int)(nu - 0.5 + U); k1 = k2 + k2 - m + 1; // delta_ml = 0 k5 = k4 + k4 - m; // delta_mr = 1 // range width of the critical left and right centre region dl = (double)(k2 - k1); dr = (double)(k5 - k4); // recurrence constants r(k) = p(k)/p(k-1) at k = k1, k2, k4+1, k5+1 r1 = (np / (double)k1 - 1.0) * (Mp - k1) / (double)(N_Mn + k1); r2 = (np / (double)k2 - 1.0) * (Mp - k2) / (double)(N_Mn + k2); r4 = (np / (double)(k4 + 1) - 1.0) * (M - k4) / (double)(N_Mn + k4 + 1); r5 = (np / (double)(k5 + 1) - 1.0) * (M - k5) / (double)(N_Mn + k5 + 1); // reciprocal values of the scale parameters of expond tail envelopes ll = System.Math.Log(r1); // expond tail left // lr = -System.Math.Log(r5); // expond tail right // // hypergeomd constant, necessary for computing function values f(k) c_pm = Fc_lnpk(m, N_Mn, M, n); // function values f(k) = p(k)/p(m) at k = k2, k4, k1, k5 f2 = System.Math.Exp(c_pm - Fc_lnpk(k2, N_Mn, M, n)); f4 = System.Math.Exp(c_pm - Fc_lnpk(k4, N_Mn, M, n)); f1 = System.Math.Exp(c_pm - Fc_lnpk(k1, N_Mn, M, n)); f5 = System.Math.Exp(c_pm - Fc_lnpk(k5, N_Mn, M, n)); // area of the two centre and the two exponential tail regions // area of the two immediate acceptance regions between k2, k4 p1 = f2 * (dl + 1.0); // immedd left p2 = f2 * dl + p1; // centre left p3 = f4 * (dr + 1.0) + p2; // immedd right p4 = f4 * dr + p3; // centre right p5 = f1 / ll + p4; // expond tail left p6 = f5 / lr + p5; // expond tail right } for (; ;) { // generate uniform number U -- U(0, p6) // case distinction corresponding to U if ((U = randomGenerator.Raw() * p6) < p2) { // centre left // immediate acceptance region R2 = [k2, m) *[0, f2), X = k2, ..d m -1 if ((W = U - p1) < 0.0) { return(k2 + (int)(U / f2)); } // immediate acceptance region R1 = [k1, k2)*[0, f1), X = k1, ..d k2-1 if ((Y = W / dl) < f1) { return(k1 + (int)(W / f1)); } // computation of candidate X < k2, and its counterpart V > k2 // either squeeze-acceptance of X or acceptance-rejection of V Dk = (int)(dl * randomGenerator.Raw()) + 1; if (Y <= f2 - Dk * (f2 - f2 / r2)) { // quick accept of return(k2 - Dk); // X = k2 - Dk } if ((W = f2 + f2 - Y) < 1.0) { // quick reject of V V = k2 + Dk; if (W <= f2 + Dk * (1.0 - f2) / (dl + 1.0)) { // quick accept of return(V); // V = k2 + Dk } if (System.Math.Log(W) <= c_pm - Fc_lnpk(V, N_Mn, M, n)) { return(V); // accept of V } } X = k2 - Dk; } else if (U < p4) { // centre right // immediate acceptance region R3 = [m, k4+1)*[0, f4), X = m, ..d k4 if ((W = U - p3) < 0.0) { return(k4 - (int)((U - p2) / f4)); } // immediate acceptance region R4 = [k4+1, k5+1)*[0, f5) if ((Y = W / dr) < f5) { return(k5 - (int)(W / f5)); } // computation of candidate X > k4, and its counterpart V < k4 // either squeeze-acceptance of X or acceptance-rejection of V Dk = (int)(dr * randomGenerator.Raw()) + 1; if (Y <= f4 - Dk * (f4 - f4 * r4)) { // quick accept of return(k4 + Dk); // X = k4 + Dk } if ((W = f4 + f4 - Y) < 1.0) { // quick reject of V V = k4 - Dk; if (W <= f4 + Dk * (1.0 - f4) / dr) { // quick accept of return(V); // V = k4 - Dk } if (System.Math.Log(W) <= c_pm - Fc_lnpk(V, N_Mn, M, n)) { return(V); // accept of V } } X = k4 + Dk; } else { Y = randomGenerator.Raw(); if (U < p5) { // expond tail left Dk = (int)(1.0 - System.Math.Log(Y) / ll); if ((X = k1 - Dk) < 0) { continue; // 0 <= X <= k1 - 1 } Y *= (U - p4) * ll; // Y -- U(0, h(x)) if (Y <= f1 - Dk * (f1 - f1 / r1)) { return(X); // quick accept of X } } else { // expond tail right Dk = (int)(1.0 - System.Math.Log(Y) / lr); if ((X = k5 + Dk) > n) { continue; // k5 + 1 <= X <= n } Y *= (U - p5) * lr; // Y -- U(0, h(x)) / if (Y <= f5 - Dk * (f5 - f5 * r5)) { return(X); // quick accept of X } } } // acceptance-rejection test of candidate X from the original area // test, whether Y <= f(X), with Y = U*h(x) and U -- U(0, 1) // log f(X) = log( m! (M - m)! (n - m)! (N - M - n + m)! ) // - log( X! (M - X)! (n - X)! (N - M - n + X)! ) // by using an external function for log k! if (System.Math.Log(Y) <= c_pm - Fc_lnpk(X, N_Mn, M, n)) { return(X); } } }