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