/// <summary> /// Prepares parameters for <see cref="TRandom"/>. /// </summary> /// <param name="weightsCount">The number of weights.</param> /// <param name="weights">Weights, or null.</param> /// <param name="cdf">The output cdf.</param> /// <remarks> /// Also remember to change <see cref="UpdateHelpers"/> when changing this method. /// </remarks> internal static void SetUp(int weightsCount, IEnumerable <double> weights, out double[] cdf) { var weightsList = (weights == null) ? Ones(weightsCount) : weights.ToList(); var weightsSum = weightsList.Sum(); // It will store the sum of all UNNORMALIZED weights. cdf = new double[weightsList.Count]; // It will store NORMALIZED cdf. var maxW = 0.0; // It will store max weight (all weights are positive). // Let's normalize all weights, if necessary. if (!TMath.AreEqual(weightsSum, 1.0)) { for (var i = 0; i < weightsList.Count; ++i) { weightsList[i] /= weightsSum; } Debug.Assert(TMath.AreEqual(weightsList.Sum(), 1.0)); } weightsSum = 0.0; // Reset weight sum to use it for cdf. // One big loop to compute all helpers needed by this distribution. for (var i = 0; i < weightsList.Count; ++i) { var w = weightsList[i]; weightsSum += w; cdf[i] = weightsSum; if (w > maxW) { maxW = w; } } }
/// <summary> /// Initializes a new instance of the <see cref="BinomialDistribution"/> class, using a /// <see cref="XorShift128Generator"/> with the specified seed value. /// </summary> /// <param name="seed"> /// An unsigned number used to calculate a starting value for the pseudo-random number sequence. /// </param> /// <param name="valueCount"> /// The parameter valueCount which is used for generation of binomial distributed random /// numbers by setting the number of equi-distributed "weights" the distribution will have. /// </param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="valueCount"/> is less than or equal to zero. /// </exception> public CategoricalDistribution(uint seed, int valueCount) : this(new XorShift128Generator(seed), valueCount) { Debug.Assert(Generator is XorShift128Generator); Debug.Assert(Generator.Seed == seed); Debug.Assert(Equals(Weights.Count, valueCount)); Debug.Assert(Weights.All(w => TMath.AreEqual(w, 1.0 / valueCount))); }
public void Doubles_MaxValue_SameOutputAsNextDouble() { var max = Rand.Next() + 1; // To avoid zero var otherGen = GetGenerator(_generator.Seed); Assert.True(_generator.Doubles(max).Take(Iterations).All(x => TMath.AreEqual(x, otherGen.NextDouble(max)))); }
/// <summary> /// Computes the unnormalized cumulative distribution function and other attributes for the /// distribution (like mean, variance, and so on). /// </summary> /// <remarks> /// Also remember to change <see cref="SetUp(int, IEnumerable{double}, out double[])"/> /// when changing this method. /// </remarks> private void UpdateHelpers() { var weightsSum = _weights.Sum(); // It will store the sum of all UNNORMALIZED weights. var cdf = new double[_weights.Count]; // It will store NORMALIZED cdf. var tmpMean = 0.0; var maxW = 0.0; // It will store max weight (all weights are positive). var maxI = 0; // It will store max weight index. // Let's normalize all weights, if necessary. if (!TMath.AreEqual(weightsSum, 1.0)) { for (var i = 0; i < _weights.Count; ++i) { _weights[i] /= weightsSum; } Debug.Assert(TMath.AreEqual(_weights.Sum(), 1.0)); } weightsSum = 0.0; // Reset weight sum to use it for cdf. // One big loop to compute all helpers needed by this distribution. for (var i = 0; i < _weights.Count; ++i) { var w = _weights[i]; weightsSum += w; cdf[i] = weightsSum; tmpMean += w * (i + 1.0); // Plus one because it is zero-based. if (w > maxW) { maxW = w; maxI = i; } } // Finalize some results... _cdf = cdf; Mean = tmpMean - 1.0; // Minus one to make it zero-based. Mode = new double[] { maxI }; var halfWeightsSum = weightsSum / 2.0; var tmpMedian = double.NaN; var tmpVar = 0.0; // We still need another loop to compute variance; as for the mean, the plus/minus one is needed. for (var i = 0; i < _weights.Count; ++i) { if (double.IsNaN(tmpMedian) && _cdf[i] >= halfWeightsSum) { tmpMedian = i; } tmpVar += _weights[i] * TMath.Square(i + 1 - Mean); } // Finalize last results... Median = tmpMedian; Variance = tmpVar - 1.0; }
public void Doubles_SameOutputAsNextDouble_AfterReset() { var otherGen = GetGenerator(_generator.Seed); Assert.True(_generator.Doubles().Take(Iterations).All(x => TMath.AreEqual(x, otherGen.NextDouble()))); _generator.Reset(); otherGen.Reset(); Assert.True(_generator.Doubles().Take(Iterations).All(x => TMath.AreEqual(x, otherGen.NextDouble()))); }
/// <summary> /// Returns a floating point random number within the specified range. /// </summary> /// <param name="minValue">The inclusive lower bound of the random number to be generated.</param> /// <param name="maxValue">The exclusive upper bound of the random number to be generated.</param> /// <returns> /// A double-precision floating point number greater than or equal to /// <paramref name="minValue"/>, and less than <paramref name="maxValue"/>; that is, the /// range of return values includes <paramref name="minValue"/> but not <paramref name="maxValue"/>. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="maxValue"/> must be greater than or equal to <paramref name="minValue"/>. /// </exception> /// <exception cref="ArgumentException"> /// The difference between <paramref name="maxValue"/> and <paramref name="minValue"/> /// cannot be <see cref="double.PositiveInfinity"/>. /// </exception> public double NextDouble(double minValue, double maxValue) { // Preconditions if (minValue > maxValue) { throw new ArgumentOutOfRangeException(nameof(minValue), ErrorMessages.MinValueGreaterThanMaxValue); } if (double.IsPositiveInfinity(maxValue - minValue)) { throw new ArgumentException(ErrorMessages.InfiniteMaxValueMinusMinValue, nameof(minValue)); } var result = minValue + NextDouble() * (maxValue - minValue); // Postconditions Debug.Assert((TMath.AreEqual(result, minValue) && TMath.AreEqual(minValue, maxValue)) || (result >= minValue && result < maxValue)); return(result); }
/// <summary> /// Initializes a new instance of the <see cref="CategoricalDistribution"/> class, using /// the specified <see cref="IGenerator"/> as underlying random number generator. /// </summary> /// <param name="generator">An <see cref="IGenerator"/> object.</param> /// <exception cref="ArgumentNullException"><paramref name="generator"/> is <see langword="null"/>.</exception> public CategoricalDistribution(IGenerator generator) : this(generator, DefaultValueCount) { Debug.Assert(ReferenceEquals(Generator, generator)); Debug.Assert(Equals(Weights.Count, DefaultValueCount)); Debug.Assert(Weights.All(w => TMath.AreEqual(w, 1.0 / DefaultValueCount))); }
/// <summary> /// Initializes a new instance of the <see cref="BinomialDistribution"/> class, using a /// <see cref="XorShift128Generator"/> as underlying random number generator. /// </summary> public CategoricalDistribution() : this(new XorShift128Generator(), DefaultValueCount) { Debug.Assert(Generator is XorShift128Generator); Debug.Assert(Equals(Weights.Count, DefaultValueCount)); Debug.Assert(Weights.All(w => TMath.AreEqual(w, 1.0 / DefaultValueCount))); }