F#中的线程安全正常随机数发生器

Fra*_*ori 5 .net random f#

需要一个从普通(高斯)分布返回样本的随机数生成器,我已经移植到F#作为John D. Cook的C#生成器的一部分:

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 () |]
Run Code Online (Sandbox Code Playgroud)

此实现工作正常但不是线程安全的.鉴于依赖于它的代码广泛使用了Thread Parallel Library,我需要使它成为线程安全的.

这对我来说并不明显,因为该方法的核心是两个可变成员,这几乎是不可或缺的.有没有其他方法来实现线程安全而不诉诸锁定?

有没有其他方法来实现仅使用不可变成员的普通伪随机生成器?

Ing*_*ahn 5

使用可变成员,你别无选择,只能使用锁.

但是,你会用一个不变的最好记录包含m_wm_z您传递给你的随机函数.他们可以返回随机值的元组和包含更新的随机成员的新​​记录.更好的是,您可以创建一个计算表达式来处理生成的random,这样您就不必担心传递随机记录了.

此外,setSeed从随机函数内调用也很糟糕.多个后续调用将返回相同的值.你只想设置一次种子.