Flow Resistant Random Number Generator in F #

A random number generator is required, which returns a sample from a normal (Gaussian) distribution, I ported the John D. Cook C # generator to F # :

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 code that depends on it makes extensive use of the parallel thread library, I need to make it thread safe.

This does not seem obvious to me, because the method is based on two mutable elements, which are largely irreplaceable. Is there any other way to ensure thread safety without resorting to locks?

Is there any other way to implement a regular pseudo-random generator using only immutable members?

+5
3

, , .

, m_w m_z, . , . , randoms, .

, setSeed . . .

+5

- System.Random, :

let random =
  let rand = System.Random()
  let locker = obj()
  fun () -> lock locker rand.Next
+3

It is better to put all m_w/ m_zand related functions in a class. Like this:

type Random = 
    let setSeed() = ...
    let randomNormal() = ...

After that, there are at least two solutions: start each thread with its own instance of the object Random; or use a class ThreadLocal<Random>for the same thing - make sure that each thread has its own instance of the class Random.

EDIT: Also, MailboxProcessorusing a method PostAndReplyis a good way to share a single generator between threads. No need to worry about synchronization.

+3
source

All Articles