/// <summary>
        /// Sample from the provided discrete probability distribution.
        /// </summary>
        /// <param name="dist">The discrete distribution to sample from.</param>
        /// <param name="rng">Random number generator.</param>
        public static int Sample(DiscreteDistribution dist, XorShiftRandom rng)
        {
            // Throw the ball and return an integer indicating the outcome.
            double sample = rng.NextDouble();
            double acc    = 0.0;

            for (int i = 0; i < dist.Probabilities.Length; i++)
            {
                acc += dist.Probabilities[i];
                if (sample < acc)
                {
                    return(dist.Labels[i]);
                }
            }

            // We might get here through floating point arithmetic rounding issues.
            // e.g. accumulator == throwValue.

            // Find a nearby non-zero probability to select.
            // Wrap around to start of array.
            for (int i = 0; i < dist.Probabilities.Length; i++)
            {
                if (0.0 != dist.Probabilities[i])
                {
                    return(dist.Labels[i]);
                }
            }

            // If we get here then we have an array of zero probabilities.
            throw new InvalidOperationException("Invalid operation. No non-zero probabilities to select.");
        }
Exemple #2
0
        /// <summary>
        /// Rounds up or down to a whole number by using the fractional part of the input value
        /// as the probability that the value will be rounded up.
        ///
        /// This is useful if we wish to round values and then sum them without generating a rounding bias.
        /// For monetary rounding this problem is solved with rounding to e.g. the nearest even number which
        /// then causes a bias towards even numbers.
        ///
        /// This solution is more appropriate for certain types of scientific values.
        /// </summary>
        public static double ProbabilisticRound(double val, XorShiftRandom rng)
        {
            double integerPart    = Math.Floor(val);
            double fractionalPart = val - integerPart;

            return(rng.NextDouble() < fractionalPart ? integerPart + 1.0 : integerPart);
        }
Exemple #3
0
        /// <summary>
        /// Construct with the provided RNG source.
        /// </summary>
        public ZigguratGaussianSampler(XorShiftRandom rng)
        {
            _rng = rng;

            // Initialise rectangle position data.
            // _x[i] and _y[i] describe the top-right position ox Box i.

            // Allocate storage. We add one to the length of _x so that we have an entry at _x[_blockCount], this avoids having
            // to do a special case test when sampling from the top box.
            _x = new double[__blockCount + 1];
            _y = new double[__blockCount];

            // Determine top right position of the base rectangle/box (the rectangle with the Gaussian tale attached).
            // We call this Box 0 or B0 for short.
            // Note. x[0] also describes the right-hand edge of B1. (See diagram).
            _x[0] = __R;
            _y[0] = GaussianPdfDenorm(__R);

            // The next box (B1) has a right hand X edge the same as B0.
            // Note. B1's height is the box area divided by its width, hence B1 has a smaller height than B0 because
            // B0's total area includes the attached distribution tail.
            _x[1] = __R;
            _y[1] = _y[0] + (__A / _x[1]);

            // Calc positions of all remaining rectangles.
            for (int i = 2; i < __blockCount; i++)
            {
                _x[i] = GaussianPdfDenormInv(_y[i - 1]);
                _y[i] = _y[i - 1] + (__A / _x[i]);
            }

            // For completeness we define the right-hand edge of a notional box 6 as being zero (a box with no area).
            _x[__blockCount] = 0.0;

            // Useful precomputed values.
            _A_Div_Y0 = __A / _y[0];
            _xComp    = new uint[__blockCount];

            // Special case for base box. _xComp[0] stores the area of B0 as a proportion of __R
            // (recalling that all segments have area __A, but that the base segment is the combination of B0 and the distribution tail).
            // Thus -xComp[0[ is the probability that a sample point is within the box part of the segment.
            _xComp[0] = (uint)(((__R * _y[0]) / __A) * (double)uint.MaxValue);

            for (int i = 1; i < __blockCount - 1; i++)
            {
                _xComp[i] = (uint)((_x[i + 1] / _x[i]) * (double)uint.MaxValue);
            }
            _xComp[__blockCount - 1] = 0;  // Shown for completeness.

            // Sanity check. Test that the top edge of the topmost rectangle is at y=1.0.
            // Note. We expect there to be a tiny drift away from 1.0 due to the inexactness of floating
            // point arithmetic.
            Debug.Assert(Math.Abs(1.0 - _y[__blockCount - 1]) < 1e-10);
        }
Exemple #4
0
 /// <summary>
 /// Randomly shuffles items within a list.
 /// </summary>
 /// <param name="list">The list to shuffle.</param>
 /// <param name="rng">Random number generator.</param>
 public static void Shuffle <T>(IList <T> list, XorShiftRandom rng)
 {
     // This approach was suggested by Jon Skeet in a dotNet newsgroup post and
     // is also the technique used by the OpenJDK. The use of rnd.Next(i+1) introduces
     // the possibility of swapping an item with itself, I suspect the reasoning behind this
     // has to do with ensuring the probability of each possible permutation is approximately equal.
     for (int i = list.Count - 1; i > 0; i--)
     {
         int swapIndex = rng.Next(i + 1);
         T   tmp       = list[swapIndex];
         list[swapIndex] = list[i];
         list[i]         = tmp;
     }
 }
 public void ReSeed(int seed)
 {
     _rng = new XorShiftRandom(seed);
 }
 /// <summary>
 /// Sample from a set of possible outcomes with equal probability, i.e. a uniform discrete distribution.
 /// </summary>
 /// <param name="numberOfOutcomes">The number of possible outcomes.</param>
 /// <param name="rng">Random number generator.</param>
 /// <returns>An integer between 0..numberOfOutcomes-1.</returns>
 public static int SampleUniformDistribution(int numberOfOutcomes, XorShiftRandom rng)
 {
     return((int)(rng.NextDouble() * numberOfOutcomes));
 }
 /// <summary>
 /// Sample from a binary distribution with the specified probability split between state false and true.
 /// </summary>
 /// <param name="probability">A probability between 0..1 that describes the probbaility of sampling boolean true.</param>
 /// <param name="rng">Random number generator.</param>
 public static bool SampleBinaryDistribution(double probability, XorShiftRandom rng)
 {
     return(rng.NextDouble() < probability);
 }