diff --git a/src/FSharp/Random.fs b/src/FSharp/Random.fs index 388edb9f..5f375624 100644 --- a/src/FSharp/Random.fs +++ b/src/FSharp/Random.fs @@ -33,18 +33,8 @@ namespace MathNet.Numerics.Random [] module Random = - /// Provides a time-dependent seed value (default behavior of System.Random) - /// WARNING: There is no randomness in this seed and quick repeated calls can cause - /// the same seed value. Do not use for cryptography! - let timeSeed () = RandomSeed.Time() - - /// Provides a seed based on unique GUIDs - /// WARNING: There is only low randomness in this seed, but at least quick repeated - /// calls will result in different seed values. Do not use for cryptography! - let seed () = RandomSeed.Guid() - /// Creates a default .Net system pRNG with a custom seed based on uinque GUIDs - let system () = new System.Random(seed()) + let system () = new System.Random(RandomSeed.Guid()) let systemSeed seed = new System.Random(seed) #if PORTABLE @@ -55,43 +45,43 @@ module Random = #endif /// Creates a Mersenne Twister 19937 pRNG with a custom seed based on uinque GUIDs - let mersenneTwister () = new MersenneTwister(seed()) :> System.Random + let mersenneTwister () = new MersenneTwister() :> System.Random let mersenneTwisterSeed (seed:int) = new MersenneTwister(seed) :> System.Random let mersenneTwisterWith seed threadSafe = new MersenneTwister(seed, threadSafe) :> System.Random /// Creates a multiply-with-carry Xorshift (Xn = a * Xn−3 + c mod 2^32) pRNG with a custom seed based on uinque GUIDs - let xorshift () = new Xorshift(seed()) :> System.Random + let xorshift () = new Xorshift() :> System.Random let xorshiftSeed (seed:int) = new Xorshift(seed) :> System.Random let xorshiftWith seed threadSafe = new Xorshift(seed, threadSafe) :> System.Random let xorshiftCustom seed threadSafe a c x1 x2 = new Xorshift(seed, threadSafe, a, c, x1, x2) :> System.Random /// Creates a Wichmann-Hill’s 1982 combined multiplicative congruential pRNG with a custom seed based on uinque GUIDs - let wh1982 () = new WH1982(seed()) :> System.Random + let wh1982 () = new WH1982() :> System.Random let wh1982Seed (seed:int) = new WH1982(seed) :> System.Random let wh1982With seed threadSafe = new WH1982(seed, threadSafe) :> System.Random /// Creates a Wichmann-Hill’s 2006 combined multiplicative congruential pRNG with a custom seed based on uinque GUIDs - let wh2006 () = new WH2006(seed()) :> System.Random + let wh2006 () = new WH2006() :> System.Random let wh2006Seed (seed:int) = new WH2006(seed) :> System.Random let wh2006With seed threadSafe = new WH2006(seed, threadSafe) :> System.Random /// Creates a Parallel Additive Lagged Fibonacci pRNG with a custom seed based on uinque GUIDs - let palf () = new Palf(seed()) :> System.Random + let palf () = new Palf() :> System.Random let palfSeed (seed:int) = new Palf(seed) :> System.Random let palfWith seed threadSafe = new Palf(seed, threadSafe, 418, 1279) :> System.Random let palfCustom seed threadSafe shortLag longLag = new Palf(seed, threadSafe, shortLag, longLag) :> System.Random /// Creates a Multiplicative congruential generator using a modulus of 2^59 and a multiplier of 13^13 pRNG with a custom seed based on uinque GUIDs - let mcg59 () = new Mcg59(seed()) :> System.Random + let mcg59 () = new Mcg59() :> System.Random let mcg59Seed (seed:int) = new Mcg59(seed) :> System.Random let mcg59With seed threadSafe = new Mcg59(seed, threadSafe) :> System.Random /// Creates a Multiplicative congruential generator using a modulus of 2^31-1 and a multiplier of 1132489760 pRNG with a custom seed based on uinque GUIDs - let mcg31m1 () = new Mcg31m1(seed()) :> System.Random + let mcg31m1 () = new Mcg31m1() :> System.Random let mcg31m1Seed (seed:int) = new Mcg31m1(seed) :> System.Random let mcg31m1With seed threadSafe = new Mcg31m1(seed, threadSafe) :> System.Random /// Creates a 32-bit combined multiple recursive generator with 2 components of order 3 pRNG with a custom seed based on uinque GUIDs - let mrg32k3a () = new Mrg32k3a(seed()) :> System.Random + let mrg32k3a () = new Mrg32k3a() :> System.Random let mrg32k3aSeed (seed:int) = new Mrg32k3a(seed) :> System.Random let mrg32k3aWith seed threadSafe = new Mrg32k3a(seed, threadSafe) :> System.Random diff --git a/src/FSharpExamples/RandomAndDistributions.fsx b/src/FSharpExamples/RandomAndDistributions.fsx index dc2788ab..30567bd6 100644 --- a/src/FSharpExamples/RandomAndDistributions.fsx +++ b/src/FSharpExamples/RandomAndDistributions.fsx @@ -1,32 +1,29 @@ -// -// Math.NET Numerics, part of the Math.NET Project -// http://numerics.mathdotnet.com -// http://github.com/mathnet/mathnet-numerics -// http://mathnetnumerics.codeplex.com -// -// 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 -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// +(** + +Math.NET Numerics: Random Numbers and Probability Distributions +=============================================================== + +The .Net base class library provides a pseudo-random number generator +for non-cryptography use in the form of the `System.Numerics` class. +Math.NET Numerics provides a few alternatives with different characteristics +in randomness, bias, sequence length and performance. All these classes +inherit from System.Random so you can use them as a replacement for +System.Random even in third-party code. + +All random number generators generate numbers in a more-or-less uniform +distribution. Often you need to sample random numbers with a different +distribution, e.g. a Gaussian. You can do that with one of the distribution +classes, or in F# also using the `Sample` module. The distribution classes +also provide a lot of other functionality around probability distributions, +like parameter estimation or evaluating the commulative distribution function. + +Initializtation +--------------- + +We need to reference Math.NET Numerics and the F# modules, and open +the namespaces for random numbers and probability distributions: + +*) #r "../../out/lib/Net40/MathNet.Numerics.dll" #r "../../out/lib/Net40/MathNet.Numerics.FSharp.dll" @@ -34,13 +31,48 @@ open MathNet.Numerics.Random open MathNet.Numerics.Distributions -// generate some seeds for random values (NOT intended for cryptography!) -let someGuidSeed = Random.seed () -let someTimeSeed = Random.timeSeed () +(** + +Random Number Generators +------------------------ + +Other than for cryptographic random numbers where you'd never want to provide +a seed, all pseudo-random numbers can be initialized with a custom seed. +The same seed causes the same number sequence to be generated, which can be +very useful if you need results to be reproducible, e.g. in testing/verification. + +If no seed is provided, System.Random uses a time based seed equivalent to the +one below. This means that all instances created within a short timeframe +(which typically spans around a thousand CPU clock cycles) will generate +exactly the same sequence. This can happen easily e.g. in parallel computing +and is often unwanted. That's why all number generators created using +Math.NET Numerics routines are by default initialized with a seed that combines +the time with a Guid (which are supposed to be generated uniquely, worldwide). + +*) + +let someTimeSeed = RandomSeed.Time() +let someGuidSeed = RandomSeed.Guid() + +(** + +Random number generators can be created using the `Random` module. Most functions +optionally accept a manual seed when using the variant with the Seed-suffix. +Some of them, like xor-shift, also have a variant with a Custom-suffix that +allow to pass additional parameters specific to that generator. + +Note that the generators should be reused when generating multiple numbers. +If you'd create a new generator each time, the numbers it generates would be +exactly as random as your seed - and thus not very random at all. +However, generators are not automatically thread-safe. They *are* thread-safe +in Math.NET Numerics by default, but that can be disabled either using a +boolean argument at creation, or by setting `Control.ThreadSafeRandomNumberGenerators`. + +*) -// some pseudo random number generators (listing incomplete; all of them are cast to the common base type, System.Random) let a = Random.system () let b = Random.systemSeed (Random.timeSeed()) +let b2 = Random.systemSeed someGuidSeed let c = Random.crypto () let d = Random.mersenneTwister () let e = Random.mersenneTwisterWith 1000 true (* thread-safe *) @@ -49,7 +81,7 @@ let g = Random.xorshiftCustom someTimeSeed false 916905990L 13579L 362436069L 77 let h = Random.wh2006 () let i = Random.palf () -// generate some uniform random values +// Generate some uniform random values let values = ( a.Next(), b.NextFullRangeInt32(), @@ -59,6 +91,20 @@ let values = ( f.NextDecimal() ) +(** + +Probability Distributions +------------------------- + +Non-uniform probability distributions can be created using their normal constructor, +some also offer static functions if there are multiple ways to parametrize them. + +Since probability distributions can also be sampled to generate random numbers +with the configured distribution, all constructors optionally accept a random generator +as last argument. + +*) + // some probability distributions let normal = Normal.WithMeanVariance(3.0, 1.5, g) let exponential = Exponential(2.4) @@ -67,7 +113,7 @@ let cauchy = Cauchy(0.0, 1.0, Random.mrg32k3aWith 10 false) let poisson = Poisson(3.0) let geometric = Geometric(0.8, Random.system()) -// generate some random samples from these distributions +// sample some random rumbers from these distributions let continuous = [ yield normal.Sample() yield exponential.Sample() @@ -79,17 +125,20 @@ let discrete = [ geometric.Sample() ] -// direct sampling (without creating a configurable distribution object) + +// direct sampling (without creating a distribution object) let u = Normal.Sample(Random.system(), 2.0, 4.0) let v = Laplace.Samples(Random.mersenneTwister(), 1.0, 3.0) |> Seq.take 100 |> List.ofSeq let w = Rayleigh.Sample(c, 1.5) let x = Hypergeometric.Sample(h, 100, 20, 5) -// probability distribution functions of the normal dist we configured above -let nd = normal.Density(4.0) (* pdf *) -let ndLn = normal.DensityLn(4.0) (* ln(pdf) *) -let nc = normal.CumulativeDistribution(4.0) (* cdf *) -let nic = normal.InverseCumulativeDistribution(0.7) (* invcdf *) +(** + +Distributions can not just be used to generate random samples. +You can use them to evaluate distribution properties or functions +with the given parametrization. + +*) // distribution properties of the gamma dist we configured above let gammaStats = ( @@ -100,3 +149,27 @@ let gammaStats = ( gamma.Skewness, gamma.Mode ) + +// probability distribution functions of the normal dist we configured above +let nd = normal.Density(4.0) (* pdf *) +let ndLn = normal.DensityLn(4.0) (* ln(pdf) *) +let nc = normal.CumulativeDistribution(4.0) (* cdf *) +let nic = normal.InverseCumulativeDistribution(0.7) (* invcdf *) + +// Distribution functions can also be evaluated without creating an object, +// but then you have to pass in the distribution parameters as first arguments: +let nd2 = Normal.PDF(3.0, sqrt 1.5, 4.0) +let ndLn2 = Normal.PDFLn(3.0, sqrt 1.5, 4.0) +let nc2 = Normal.CDF(3.0, sqrt 1.5, 4.0) +let nic2 = Normal.InvCDF(3.0, sqrt 1.5, 0.7) + +(** + +Some of the distributions also have routines for maximum-likelihood parameter +estimation from a set of samples: + +*) + +let estimation = LogNormal.Estimate([| 2.0; 1.5; 2.1; 1.2; 3.0; 2.4; 1.8 |]) +let mean, variance = estimation.Mean, estimation.Variance +let moreSamples = estimation.Samples() |> Seq.take 10 |> Seq.toArray