From 11519beb84d423209659fd4a2b1eee065c41a9f2 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 19 Dec 2013 19:29:53 +0100 Subject: [PATCH] Random: tweak sampling (based on a lot of benchmarks), new robust seeds --- src/FSharp/Random.fs | 12 ++-- src/Numerics/Random/Mcg31m1.cs | 6 +- src/Numerics/Random/Mcg59.cs | 6 +- src/Numerics/Random/MersenneTwister.cs | 11 ++-- src/Numerics/Random/Mrg32k3a.cs | 6 +- src/Numerics/Random/Palf.cs | 6 +- src/Numerics/Random/RandomSeed.cs | 27 ++++++++ src/Numerics/Random/RandomSource.cs | 30 +++++++-- src/Numerics/Random/SystemRandomSource.cs | 80 ++++++++++++++++++++++- src/Numerics/Random/WH1982.cs | 6 +- src/Numerics/Random/WH2006.cs | 6 +- src/Numerics/Random/Xorshift.cs | 10 +-- 12 files changed, 172 insertions(+), 34 deletions(-) diff --git a/src/FSharp/Random.fs b/src/FSharp/Random.fs index da2c6f01..f6253d2d 100644 --- a/src/FSharp/Random.fs +++ b/src/FSharp/Random.fs @@ -4,7 +4,7 @@ // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // -// Copyright (c) 2009-2012 Math.NET +// Copyright (c) 2009-2013 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -33,10 +33,12 @@ namespace MathNet.Numerics.Random [] module Random = - /// Returns the default mersenne twister, thread-safe and also thread-locally shared - let shared = MersenneTwister.Default :> System.Random - let samples length = MersenneTwister.Samples(length, RandomSeed.Guid()) - let sampleSeq () = MersenneTwister.SampleSequence(RandomSeed.Guid()) + /// Returns the default random source, thread-safe and also thread-locally shared + let shared = SystemRandomSource.Default :> System.Random + + /// Default sampling, efficient but without custom seed (uses robust seeds internally) + let samples length = SystemRandomSource.Samples(length) + let sampleSeq () = SystemRandomSource.SampleSequence() /// Creates a default .Net system pRNG with a custom seed based on uinque GUIDs let system () = new SystemRandomSource() :> System.Random diff --git a/src/Numerics/Random/Mcg31m1.cs b/src/Numerics/Random/Mcg31m1.cs index 6770f87c..d169672d 100644 --- a/src/Numerics/Random/Mcg31m1.cs +++ b/src/Numerics/Random/Mcg31m1.cs @@ -46,7 +46,7 @@ namespace MathNet.Numerics.Random /// Initializes a new instance of the class using /// a seed based on time and unique GUIDs. /// - public Mcg31m1() : this(RandomSeed.Guid()) + public Mcg31m1() : this(RandomSeed.Robust()) { } @@ -55,7 +55,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public Mcg31m1(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public Mcg31m1(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -105,6 +105,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { if (seed == 0) @@ -125,6 +126,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { if (seed == 0) diff --git a/src/Numerics/Random/Mcg59.cs b/src/Numerics/Random/Mcg59.cs index 9b20083c..5449be1b 100644 --- a/src/Numerics/Random/Mcg59.cs +++ b/src/Numerics/Random/Mcg59.cs @@ -46,7 +46,7 @@ namespace MathNet.Numerics.Random /// Initializes a new instance of the class using /// a seed based on time and unique GUIDs. /// - public Mcg59() : this(RandomSeed.Guid()) + public Mcg59() : this(RandomSeed.Robust()) { } @@ -55,7 +55,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public Mcg59(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public Mcg59(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -107,6 +107,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { if (seed == 0) @@ -127,6 +128,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { if (seed == 0) diff --git a/src/Numerics/Random/MersenneTwister.cs b/src/Numerics/Random/MersenneTwister.cs index 1621163b..19457160 100644 --- a/src/Numerics/Random/MersenneTwister.cs +++ b/src/Numerics/Random/MersenneTwister.cs @@ -66,7 +66,6 @@ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ -using System; using System.Collections.Generic; using System.Threading; @@ -129,7 +128,7 @@ namespace MathNet.Numerics.Random /// If the seed value is zero, it is set to one. Uses the /// value of to /// set whether the instance is thread safe. - public MersenneTwister() : this(RandomSeed.Guid()) + public MersenneTwister() : this(RandomSeed.Robust()) { } @@ -138,7 +137,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public MersenneTwister(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public MersenneTwister(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -176,13 +175,13 @@ namespace MathNet.Numerics.Random { if (DefaultInstance == null) { - DefaultInstance = new MersenneTwister(RandomSeed.Guid(), true); + DefaultInstance = new MersenneTwister(RandomSeed.Robust(), true); } return DefaultInstance; } } #else - static readonly ThreadLocal DefaultInstance = new ThreadLocal(() => new MersenneTwister(RandomSeed.Guid(), true)); + static readonly ThreadLocal DefaultInstance = new ThreadLocal(() => new MersenneTwister(RandomSeed.Robust(), true)); /// /// Default instance, thread-safe. @@ -335,6 +334,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { uint[] t = new uint[624]; @@ -388,6 +388,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { uint[] t = new uint[624]; diff --git a/src/Numerics/Random/Mrg32k3a.cs b/src/Numerics/Random/Mrg32k3a.cs index a52b08be..8c682335 100644 --- a/src/Numerics/Random/Mrg32k3a.cs +++ b/src/Numerics/Random/Mrg32k3a.cs @@ -60,7 +60,7 @@ namespace MathNet.Numerics.Random /// If the seed value is zero, it is set to one. Uses the /// value of to /// set whether the instance is thread safe. - public Mrg32k3a() : this(RandomSeed.Guid()) + public Mrg32k3a() : this(RandomSeed.Robust()) { } @@ -69,7 +69,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public Mrg32k3a(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public Mrg32k3a(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -144,6 +144,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { double x1 = 1; @@ -186,6 +187,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { double x1 = 1; diff --git a/src/Numerics/Random/Palf.cs b/src/Numerics/Random/Palf.cs index 40fa6f65..40bed9c2 100644 --- a/src/Numerics/Random/Palf.cs +++ b/src/Numerics/Random/Palf.cs @@ -67,7 +67,7 @@ namespace MathNet.Numerics.Random /// If the seed value is zero, it is set to one. Uses the /// value of to /// set whether the instance is thread safe. - public Palf() : this(RandomSeed.Guid(), Control.ThreadSafeRandomNumberGenerators, DefaultShortLag, DefaultLongLag) + public Palf() : this(RandomSeed.Robust(), Control.ThreadSafeRandomNumberGenerators, DefaultShortLag, DefaultLongLag) { } @@ -76,7 +76,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public Palf(bool threadSafe) : this(RandomSeed.Guid(), threadSafe, DefaultShortLag, DefaultLongLag) + public Palf(bool threadSafe) : this(RandomSeed.Robust(), threadSafe, DefaultShortLag, DefaultLongLag) { } @@ -226,6 +226,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { if (seed == 0) @@ -275,6 +276,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { if (seed == 0) diff --git a/src/Numerics/Random/RandomSeed.cs b/src/Numerics/Random/RandomSeed.cs index cb0dbe79..c6b39a0d 100644 --- a/src/Numerics/Random/RandomSeed.cs +++ b/src/Numerics/Random/RandomSeed.cs @@ -4,6 +4,14 @@ namespace MathNet.Numerics.Random { public static class RandomSeed { + static readonly object Lock = new object(); + +#if PORTABLE + static readonly System.Random MasterRng = new System.Random(); +#else + static readonly System.Security.Cryptography.RandomNumberGenerator MasterRng = new System.Security.Cryptography.RNGCryptoServiceProvider(); +#endif + /// /// Provides a time-dependent seed value, matching the default behavior of System.Random. /// WARNING: There is no randomness in this seed and quick repeated calls can cause @@ -23,5 +31,24 @@ namespace MathNet.Numerics.Random { return Environment.TickCount ^ System.Guid.NewGuid().GetHashCode(); } + + /// + /// Provides a seed based on an internal random number generator (crypto if available), time and unique GUIDs. + /// WARNING: There is only medium randomness in this seed, but quick repeated + /// calls will result in different seed values. Do not use for cryptography! + /// + public static int Robust() + { + lock (Lock) + { +#if PORTABLE + return MasterRng.NextFullRangeInt32() ^ Environment.TickCount ^ System.Guid.NewGuid().GetHashCode(); +#else + var bytes = new byte[4]; + MasterRng.GetBytes(bytes); + return BitConverter.ToInt32(bytes, 0); +#endif + } + } } } diff --git a/src/Numerics/Random/RandomSource.cs b/src/Numerics/Random/RandomSource.cs index cf9998e8..24830e8d 100644 --- a/src/Numerics/Random/RandomSource.cs +++ b/src/Numerics/Random/RandomSource.cs @@ -29,6 +29,7 @@ // using System; +using System.Collections.Generic; using MathNet.Numerics.Properties; namespace MathNet.Numerics.Random @@ -48,7 +49,7 @@ namespace MathNet.Numerics.Random /// the value of to set whether /// the instance is thread safe or not. /// - protected RandomSource() : base(RandomSeed.Guid()) + protected RandomSource() : base(RandomSeed.Robust()) { _threadSafe = Control.ThreadSafeRandomNumberGenerators; } @@ -59,18 +60,15 @@ namespace MathNet.Numerics.Random /// if set to true , the class is thread safe. /// Thread safe instances are two and half times slower than non-thread /// safe classes. - protected RandomSource(bool threadSafe) : base(RandomSeed.Guid()) + protected RandomSource(bool threadSafe) : base(RandomSeed.Robust()) { _threadSafe = threadSafe; } /// - /// Returns an array of uniformly distributed random doubles in the interval [0.0,1.0]. + /// Returns an array of uniform random numbers greater than or equal to 0.0 and less than 1.0. /// /// The size of the array. - /// - /// An array of uniformly distributed random doubles in the interval [0.0,1.0]. - /// /// if n is not greater than 0. public double[] NextDoubles(int n) { @@ -100,6 +98,26 @@ namespace MathNet.Numerics.Random return ret; } + /// + /// Returns an infinite sequence of uniform random numbers greater than or equal to 0.0 and less than 1.0. + /// + public IEnumerable NextDoubleSequence() + { + for (int i = 0; i < 64; i++) + { + yield return NextDouble(); + } + + while (true) + { + var buffer = NextDoubles(64); + for (int i = 0; i < buffer.Length; i++) + { + yield return buffer[i]; + } + } + } + /// /// Returns a nonnegative random number. /// diff --git a/src/Numerics/Random/SystemRandomSource.cs b/src/Numerics/Random/SystemRandomSource.cs index 8dae51dd..4f80f336 100644 --- a/src/Numerics/Random/SystemRandomSource.cs +++ b/src/Numerics/Random/SystemRandomSource.cs @@ -29,6 +29,8 @@ // using System.Collections.Generic; +using System.Threading; +using MathNet.Numerics.Threading; namespace MathNet.Numerics.Random { @@ -42,7 +44,7 @@ namespace MathNet.Numerics.Random /// /// Construct a new random number generator with a random seed. /// - public SystemRandomSource() : this(RandomSeed.Guid()) + public SystemRandomSource() : this(RandomSeed.Robust()) { } @@ -50,7 +52,7 @@ namespace MathNet.Numerics.Random /// Construct a new random number generator with random seed. /// /// if set to true , the class is thread safe. - public SystemRandomSource(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public SystemRandomSource(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -73,6 +75,36 @@ namespace MathNet.Numerics.Random _random = new System.Random(seed); } +#if PORTABLE + [ThreadStatic] + static SystemRandomSource DefaultInstance; + + /// + /// Default instance, thread-safe. + /// + public static SystemRandomSource Default + { + get + { + if (DefaultInstance == null) + { + DefaultInstance = new SystemRandomSource(RandomSeed.Robust(), true); + } + return DefaultInstance; + } + } +#else + static readonly ThreadLocal DefaultInstance = new ThreadLocal(() => new SystemRandomSource(RandomSeed.Robust(), true)); + + /// + /// Default instance, thread-safe. + /// + public static SystemRandomSource Default + { + get { return DefaultInstance.Value; } + } +#endif + /// /// Returns a random number between 0.0 and 1.0. /// @@ -84,9 +116,52 @@ namespace MathNet.Numerics.Random return _random.NextDouble(); } + /// + /// Returns an array of uniform random numbers greater than or equal to 0.0 and less than 1.0. + /// + /// Parallelized on large length, but also supports being called in parallel from multiple threads + public static double[] Samples(int length) + { + if (length < 2048) + { + return Default.NextDoubles(length); + } + + var data = new double[length]; + CommonParallel.For(0, length, length >= 65536 ? 8192 : length >= 16384 ? 2048 : 1024, (a, b) => + { + var rnd = new System.Random(RandomSeed.Robust()); + for (int i = a; i < b; i++) + { + data[i] = rnd.NextDouble(); + } + }); + return data; + } + + /// + /// Returns an infinite sequence of uniform random numbers greater than or equal to 0.0 and less than 1.0. + /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. + public static IEnumerable SampleSequence() + { + var rnd1 = Default; + for (int i = 0; i < 128; i++) + { + yield return rnd1.NextDouble(); + } + + var rnd2 = new System.Random(RandomSeed.Robust()); + while (true) + { + yield return rnd2.NextDouble(); + } + } + /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { var rnd = new System.Random(seed); @@ -102,6 +177,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { var rnd = new System.Random(seed); diff --git a/src/Numerics/Random/WH1982.cs b/src/Numerics/Random/WH1982.cs index 15083501..abcc74fe 100644 --- a/src/Numerics/Random/WH1982.cs +++ b/src/Numerics/Random/WH1982.cs @@ -54,7 +54,7 @@ namespace MathNet.Numerics.Random /// Initializes a new instance of the class using /// a seed based on time and unique GUIDs. /// - public WH1982() : this(RandomSeed.Guid()) + public WH1982() : this(RandomSeed.Robust()) { } @@ -63,7 +63,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public WH1982(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public WH1982(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -119,6 +119,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { if (seed == 0) @@ -145,6 +146,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { if (seed == 0) diff --git a/src/Numerics/Random/WH2006.cs b/src/Numerics/Random/WH2006.cs index 37afd8a5..376e63c5 100644 --- a/src/Numerics/Random/WH2006.cs +++ b/src/Numerics/Random/WH2006.cs @@ -57,7 +57,7 @@ namespace MathNet.Numerics.Random /// Initializes a new instance of the class using /// a seed based on time and unique GUIDs. /// - public WH2006() : this(RandomSeed.Guid()) + public WH2006() : this(RandomSeed.Robust()) { } @@ -66,7 +66,7 @@ namespace MathNet.Numerics.Random /// a seed based on time and unique GUIDs. /// /// if set to true , the class is thread safe. - public WH2006(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public WH2006(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -122,6 +122,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. public static double[] Samples(int length, int seed) { if (seed == 0) @@ -150,6 +151,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. public static IEnumerable SampleSequence(int seed) { if (seed == 0) diff --git a/src/Numerics/Random/Xorshift.cs b/src/Numerics/Random/Xorshift.cs index 56b0f6e5..cf02bff1 100644 --- a/src/Numerics/Random/Xorshift.cs +++ b/src/Numerics/Random/Xorshift.cs @@ -105,7 +105,7 @@ namespace MathNet.Numerics.Random /// X1 = 77465321 /// X2 = 362436069 /// - public Xorshift() : this(RandomSeed.Guid()) + public Xorshift() : this(RandomSeed.Robust()) { } @@ -122,7 +122,7 @@ namespace MathNet.Numerics.Random /// set whether the instance is thread safe. /// Note: must be less than . /// - public Xorshift(long a, long c, long x1, long x2) : this(RandomSeed.Guid(), a, c, x1, x2) + public Xorshift(long a, long c, long x1, long x2) : this(RandomSeed.Robust(), a, c, x1, x2) { } @@ -139,7 +139,7 @@ namespace MathNet.Numerics.Random /// X1 = 77465321 /// X2 = 362436069 /// - public Xorshift(bool threadSafe) : this(RandomSeed.Guid(), threadSafe) + public Xorshift(bool threadSafe) : this(RandomSeed.Robust(), threadSafe) { } @@ -153,7 +153,7 @@ namespace MathNet.Numerics.Random /// The initial value if X1. /// The initial value if X2. /// must be less than . - public Xorshift(bool threadSafe, long a, long c, long x1, long x2) : this(RandomSeed.Guid(), threadSafe, a, c, x1, x2) + public Xorshift(bool threadSafe, long a, long c, long x1, long x2) : this(RandomSeed.Robust(), threadSafe, a, c, x1, x2) { } @@ -266,6 +266,7 @@ namespace MathNet.Numerics.Random /// /// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads. [CLSCompliant(false)] public static double[] Samples(int length, int seed, ulong a = ASeed, ulong c = CSeed, ulong x1 = YSeed, ulong x2 = ZSeed) { @@ -296,6 +297,7 @@ namespace MathNet.Numerics.Random /// /// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0. /// + /// Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each. [CLSCompliant(false)] public static IEnumerable SampleSequence(int seed, ulong a = ASeed, ulong c = CSeed, ulong x1 = YSeed, ulong x2 = ZSeed) {