Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread-safe normal random number generator in F#

Tags:

.net

random

f#

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?

like image 963
Francesco De Vittori Avatar asked May 02 '12 15:05

Francesco De Vittori


2 Answers

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.

like image 194
IngisKahn Avatar answered Sep 28 '22 16:09

IngisKahn


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
like image 45
Daniel Avatar answered Sep 28 '22 16:09

Daniel