Beispiel #1
0
        /// <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 &gt;= 0</tt> and <tt>n &lt;= 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 &gt;= 0 and &lt;=<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>&gt;= 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;
            }
        }
Beispiel #2
0
        /// <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 &gt;= 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 &lt; p &lt; 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 &gt; 0.</param>
        /// <param name="k">must be &gt; 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 &gt;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 &gt; 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)));
 }
Beispiel #12
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 &gt;= 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 &gt;= 0 and &lt;=<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>&gt;= 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;
                }
            }
        }
Beispiel #13
0
        /// <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 &gt;= 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 &gt;= 0 and &lt;=<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>&gt;= 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));
 }
Beispiel #15
0
        /// <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);
                    }
                }
            }
        }
Beispiel #16
0
        /// <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()));
 }
Beispiel #19
0
        /// <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);
            }
        }
Beispiel #20
0
        /// <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);
                }
            }
        }