Needing a random number generator that returns a sample from a normal (Gaussian) distribution, I've ported to F# a portion of John D. Cook's C# generator:
let mutable m_w = 521288629u
let mutable m_z = 362436069u
let private getUint () =
m_z <- 36969u * (m_z &&& 65535u) + (m_z >>> 16)
m_w <- 18000u * (m_w &&& 65535u) + (m_w >>> 16)
(m_z <<< 16) + m_w
let private setSeed () =
let dt = System.DateTime.Now
let x = dt.ToFileTime ()
m_w <- uint32 (x >>> 16)
m_z <- uint32 (x % 4294967296L)
let private getUniform () =
let u = getUint ()
(float u + 1.) * 2.328306435454494e-10
let private randomNormal () =
let u1 = getUniform ()
let u2 = getUniform ()
let r = sqrt (-2. * (log u1))
let theta = 2. * System.Math.PI * u2
r * sin (theta)
/// Returns a normal (Gaussian) random sample with mean 0 and standard deviation 1
let randn () =
setSeed ()
randomNormal ()
/// Returns an array of normal (Gaussian) random samples
let randns n m =
setSeed ()
[| for i in 0 .. n - 1 -> randomNormal () |]
This implementation works fine but is not thread safe. Given that the code that depends on it makes extensive use of the Thread Parallel Library, I need to make it thread safe.
This does not look obvious to me because at the core of the method lie two mutable members which are pretty much indispensable. Is there any other way to achieve thread-safety without resorting to locks?
Is there any other way to implement a normal pseudo-random generator using only immutable members?
Using mutable members, you'd have no choice but use a lock.
However, you'd be better off with an immutable record containing m_w
and m_z
that you pass to your random functions. They could return a tuple of your random value and a new record containing the updated random members. Better yet, you could create a computation expression to handle generating randoms so you won't have to worry about passing the random record around.
Also, calling setSeed
from within your random functions is bad. Multiple subsequent calls will return the same vale. You only want to set your seed once.
Here's a trivial thread-safe solution using System.Random, if it's any help:
let random =
let rand = System.Random()
let locker = obj()
fun () -> lock locker rand.Next
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With