Browse Source

Random: tweak sampling (based on a lot of benchmarks), new robust seeds

optimization-3
Christoph Ruegg 13 years ago
parent
commit
11519beb84
  1. 12
      src/FSharp/Random.fs
  2. 6
      src/Numerics/Random/Mcg31m1.cs
  3. 6
      src/Numerics/Random/Mcg59.cs
  4. 11
      src/Numerics/Random/MersenneTwister.cs
  5. 6
      src/Numerics/Random/Mrg32k3a.cs
  6. 6
      src/Numerics/Random/Palf.cs
  7. 27
      src/Numerics/Random/RandomSeed.cs
  8. 30
      src/Numerics/Random/RandomSource.cs
  9. 80
      src/Numerics/Random/SystemRandomSource.cs
  10. 6
      src/Numerics/Random/WH1982.cs
  11. 6
      src/Numerics/Random/WH2006.cs
  12. 10
      src/Numerics/Random/Xorshift.cs

12
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
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
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

6
src/Numerics/Random/Mcg31m1.cs

@ -46,7 +46,7 @@ namespace MathNet.Numerics.Random
/// Initializes a new instance of the <see cref="Mcg31m1"/> class using
/// a seed based on time and unique GUIDs.
/// </summary>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
public Mcg31m1(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public Mcg31m1(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -105,6 +105,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
if (seed == 0)
@ -125,6 +126,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
if (seed == 0)

6
src/Numerics/Random/Mcg59.cs

@ -46,7 +46,7 @@ namespace MathNet.Numerics.Random
/// Initializes a new instance of the <see cref="Mcg59"/> class using
/// a seed based on time and unique GUIDs.
/// </summary>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
public Mcg59(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public Mcg59(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -107,6 +107,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
if (seed == 0)
@ -127,6 +128,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
if (seed == 0)

11
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
/// <remarks>If the seed value is zero, it is set to one. Uses the
/// value of <see cref="Control.ThreadSafeRandomNumberGenerators"/> to
/// set whether the instance is thread safe.</remarks>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
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<MersenneTwister> DefaultInstance = new ThreadLocal<MersenneTwister>(() => new MersenneTwister(RandomSeed.Guid(), true));
static readonly ThreadLocal<MersenneTwister> DefaultInstance = new ThreadLocal<MersenneTwister>(() => new MersenneTwister(RandomSeed.Robust(), true));
/// <summary>
/// Default instance, thread-safe.
@ -335,6 +334,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
uint[] t = new uint[624];
@ -388,6 +388,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
uint[] t = new uint[624];

6
src/Numerics/Random/Mrg32k3a.cs

@ -60,7 +60,7 @@ namespace MathNet.Numerics.Random
/// <remarks>If the seed value is zero, it is set to one. Uses the
/// value of <see cref="Control.ThreadSafeRandomNumberGenerators"/> to
/// set whether the instance is thread safe.</remarks>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
public Mrg32k3a(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public Mrg32k3a(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -144,6 +144,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
double x1 = 1;
@ -186,6 +187,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
double x1 = 1;

6
src/Numerics/Random/Palf.cs

@ -67,7 +67,7 @@ namespace MathNet.Numerics.Random
/// <remarks>If the seed value is zero, it is set to one. Uses the
/// value of <see cref="Control.ThreadSafeRandomNumberGenerators"/> to
/// set whether the instance is thread safe.</remarks>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
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
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
if (seed == 0)
@ -275,6 +276,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
if (seed == 0)

27
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
/// <summary>
/// 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();
}
/// <summary>
/// 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!
/// </summary>
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
}
}
}
}

30
src/Numerics/Random/RandomSource.cs

@ -29,6 +29,7 @@
// </copyright>
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 <see cref="Control.ThreadSafeRandomNumberGenerators"/> to set whether
/// the instance is thread safe or not.
/// </summary>
protected RandomSource() : base(RandomSeed.Guid())
protected RandomSource() : base(RandomSeed.Robust())
{
_threadSafe = Control.ThreadSafeRandomNumberGenerators;
}
@ -59,18 +60,15 @@ namespace MathNet.Numerics.Random
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
/// <remarks>Thread safe instances are two and half times slower than non-thread
/// safe classes.</remarks>
protected RandomSource(bool threadSafe) : base(RandomSeed.Guid())
protected RandomSource(bool threadSafe) : base(RandomSeed.Robust())
{
_threadSafe = threadSafe;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="n">The size of the array.</param>
/// <returns>
/// An array of uniformly distributed random doubles in the interval [0.0,1.0].
/// </returns>
/// <exception cref="ArgumentException">if n is not greater than 0.</exception>
public double[] NextDoubles(int n)
{
@ -100,6 +98,26 @@ namespace MathNet.Numerics.Random
return ret;
}
/// <summary>
/// Returns an infinite sequence of uniform random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
public IEnumerable<double> 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];
}
}
}
/// <summary>
/// Returns a nonnegative random number.
/// </summary>

80
src/Numerics/Random/SystemRandomSource.cs

@ -29,6 +29,8 @@
// </copyright>
using System.Collections.Generic;
using System.Threading;
using MathNet.Numerics.Threading;
namespace MathNet.Numerics.Random
{
@ -42,7 +44,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Construct a new random number generator with a random seed.
/// </summary>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
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;
/// <summary>
/// Default instance, thread-safe.
/// </summary>
public static SystemRandomSource Default
{
get
{
if (DefaultInstance == null)
{
DefaultInstance = new SystemRandomSource(RandomSeed.Robust(), true);
}
return DefaultInstance;
}
}
#else
static readonly ThreadLocal<SystemRandomSource> DefaultInstance = new ThreadLocal<SystemRandomSource>(() => new SystemRandomSource(RandomSeed.Robust(), true));
/// <summary>
/// Default instance, thread-safe.
/// </summary>
public static SystemRandomSource Default
{
get { return DefaultInstance.Value; }
}
#endif
/// <summary>
/// Returns a random number between 0.0 and 1.0.
/// </summary>
@ -84,9 +116,52 @@ namespace MathNet.Numerics.Random
return _random.NextDouble();
}
/// <summary>
/// Returns an array of uniform random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Parallelized on large length, but also supports being called in parallel from multiple threads</remarks>
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;
}
/// <summary>
/// Returns an infinite sequence of uniform random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> 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();
}
}
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
var rnd = new System.Random(seed);
@ -102,6 +177,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
var rnd = new System.Random(seed);

6
src/Numerics/Random/WH1982.cs

@ -54,7 +54,7 @@ namespace MathNet.Numerics.Random
/// Initializes a new instance of the <see cref="WH1982"/> class using
/// a seed based on time and unique GUIDs.
/// </summary>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
public WH1982(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public WH1982(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -119,6 +119,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
if (seed == 0)
@ -145,6 +146,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
if (seed == 0)

6
src/Numerics/Random/WH2006.cs

@ -57,7 +57,7 @@ namespace MathNet.Numerics.Random
/// Initializes a new instance of the <see cref="WH2006"/> class using
/// a seed based on time and unique GUIDs.
/// </summary>
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.
/// </summary>
/// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
public WH2006(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public WH2006(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -122,6 +122,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
public static double[] Samples(int length, int seed)
{
if (seed == 0)
@ -150,6 +151,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
public static IEnumerable<double> SampleSequence(int seed)
{
if (seed == 0)

10
src/Numerics/Random/Xorshift.cs

@ -105,7 +105,7 @@ namespace MathNet.Numerics.Random
/// <item>X1 = 77465321</item>
/// <item>X2 = 362436069</item>
/// </list></remarks>
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: <paramref name="c"/> must be less than <paramref name="a"/>.
/// </remarks>
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
/// <item>X1 = 77465321</item>
/// <item>X2 = 362436069</item>
/// </list></remarks>
public Xorshift(bool threadSafe) : this(RandomSeed.Guid(), threadSafe)
public Xorshift(bool threadSafe) : this(RandomSeed.Robust(), threadSafe)
{
}
@ -153,7 +153,7 @@ namespace MathNet.Numerics.Random
/// <param name="x1">The initial value if X1.</param>
/// <param name="x2">The initial value if X2.</param>
/// <remarks><paramref name="c"/> must be less than <paramref name="a"/>.</remarks>
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
/// <summary>
/// Returns an array of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads.</remarks>
[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
/// <summary>
/// Returns an infinite sequence of random numbers greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <remarks>Supports being called in parallel from multiple threads, but the result must be enumerated from a single thread each.</remarks>
[CLSCompliant(false)]
public static IEnumerable<double> SampleSequence(int seed, ulong a = ASeed, ulong c = CSeed, ulong x1 = YSeed, ulong x2 = ZSeed)
{

Loading…
Cancel
Save