/// <summary> /// Get the next sample point from the gaussian distribution. /// </summary> public double NextDouble() { if (null != _spareValue) { double tmp = _spareValue.Value; _spareValue = null; return(tmp); } // Generate two new gaussian values. double x, y, sqr; // We need a non-zero random point inside the unit circle. do { x = 2.0 * _rng.NextDouble() - 1.0; y = 2.0 * _rng.NextDouble() - 1.0; sqr = x * x + y * y; }while(sqr > 1.0 || sqr == 0); // Make the Box-Muller transformation. double fac = Math.Sqrt(-2.0 * Math.Log(sqr) / sqr); _spareValue = x * fac; return(y * fac); }
/// <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> /// Get the next sample value from the gaussian distribution. /// </summary> public double NextSample() { for (;;) { // Select box at random. byte u = _rng.NextByte(); int i = (int)(u & 0x7F); double sign = ((u & 0x80) == 0) ? -1.0 : 1.0; // Generate uniform random value with range [0,0xffffffff]. uint u2 = _rng.NextUInt(); // Special case for the base segment. if (0 == i) { if (u2 < _xComp[0]) { // Generated x is within R0. return(u2 * __UIntToU * _A_Div_Y0 * sign); } // Generated x is in the tail of the distribution. return(SampleTail() * sign); } // All other segments. if (u2 < _xComp[i]) { // Generated x is within the rectangle. return(u2 * __UIntToU * _x[i] * sign); } // Generated x is outside of the rectangle. // Generate a random y coordinate and test if our (x,y) is within the distribution curve. // This execution path is relatively slow/expensive (makes a call to Math.Exp()) but relatively rarely executed, // although more often than the 'tail' path (above). double x = u2 * __UIntToU * _x[i]; if (_y[i - 1] + ((_y[i] - _y[i - 1]) * _rng.NextDouble()) < GaussianPdfDenorm(x)) { return(x * sign); } } }
/// <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); }