diff --git a/ASFFreeGames/RandomUtils.cs b/ASFFreeGames/RandomUtils.cs index e9ef9ae..de69199 100644 --- a/ASFFreeGames/RandomUtils.cs +++ b/ASFFreeGames/RandomUtils.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Security.Cryptography; namespace Maxisoft.ASF; @@ -8,35 +10,6 @@ namespace Maxisoft.ASF; #nullable enable public static class RandomUtils { - /// - /// Generates a random number from a normal distribution with the specified mean and standard deviation. - /// - /// The random number generator to use. - /// The mean of the normal distribution. - /// The standard deviation of the normal distribution. - /// A random number from the normal distribution. - /// - /// This method uses the Box-Muller transform to convert two uniformly distributed random numbers into two normally distributed random numbers. - /// - public static double NextGaussian([NotNull] this RandomNumberGenerator random, double mean, double standardDeviation) { - Debug.Assert(random != null, nameof(random) + " != null"); - - // Generate two uniform random numbers - Span bytes = stackalloc byte[8]; - random.GetBytes(bytes); - double u1 = BitConverter.ToUInt32(bytes) / (double) uint.MaxValue; - random.GetBytes(bytes); - double u2 = BitConverter.ToUInt32(bytes) / (double) uint.MaxValue; - - // Apply the Box-Muller formula - double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - - // Scale and shift to get a random number with the desired mean and standard deviation - double randNormal = mean + (standardDeviation * randStdNormal); - - return randNormal; - } - internal sealed class GaussianRandom : RandomNumberGenerator { // A flag to indicate if there is a stored value for the next Gaussian number private bool HasNextGaussian; @@ -55,23 +28,20 @@ private double NextDouble() { return NextGaussianValue; } - // Generate two uniform random numbers - Span bytes = stackalloc byte[8]; - GetBytes(bytes); - float u1 = BitConverter.ToUInt32(bytes) / (float) uint.MaxValue; - GetBytes(bytes); - float u2 = BitConverter.ToUInt32(bytes) / (float) uint.MaxValue; + Span bytes = stackalloc byte[16]; + Fill(bytes); + Span ulongs = MemoryMarshal.Cast(bytes); + double u1 = ulongs[0] / (double) ulong.MaxValue; + double u2 = ulongs[1] / (double) ulong.MaxValue; // Apply the Box-Muller formula - float r = MathF.Sqrt(-2.0f * MathF.Log(u1)); - float theta = 2.0f * MathF.PI * u2; + double r = Math.Sqrt(-2.0f * Math.Log(u1)); + double theta = 2.0 * Math.PI * u2; - // Store one of the values for next time - NextGaussianValue = r * MathF.Sin(theta); + NextGaussianValue = r * Math.Sin(theta); HasNextGaussian = true; - // Return the other value - return r * MathF.Cos(theta); + return r * Math.Cos(theta); } ///