2012-05-02 38 views
5

Cần một bộ tạo số ngẫu nhiên mà trả về một mẫu từ một (Gaussian) phân phối bình thường, tôi đã chuyển đến F # một phần của John D. Cook's C# generator:Chủ đề an toàn trong 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() |] 

thi này hoạt động tốt nhưng không phải là chủ đề an toàn. Cho rằng mã phụ thuộc vào nó làm cho việc sử dụng rộng rãi Thư viện song song luồng, tôi cần phải làm cho chuỗi đó an toàn.

Điều này không rõ ràng đối với tôi vì cốt lõi của phương pháp là hai thành viên có thể thay đổi được khá nhiều không thể thiếu. Có cách nào khác để đạt được an toàn luồng mà không cần đến khóa không?

Có cách nào khác để triển khai trình tạo giả ngẫu nhiên bình thường chỉ sử dụng các thành viên không thay đổi được không?

Trả lời

5

Sử dụng thành viên có thể thay đổi, bạn không có lựa chọn nào khác ngoài việc sử dụng khóa.

Tuy nhiên, bạn nên làm tốt hơn với sự bất biến record chứa m_wm_z mà bạn chuyển đến các chức năng ngẫu nhiên của mình. Họ có thể trả về một bộ giá trị ngẫu nhiên của bạn và một bản ghi mới chứa các thành viên ngẫu nhiên được cập nhật. Tốt hơn, bạn có thể tạo ra một computation expression để xử lý các randoms tạo ra, do đó bạn sẽ không phải lo lắng về việc vượt qua các bản ghi ngẫu nhiên xung quanh.

Ngoài ra, gọi setSeed từ bên trong các chức năng ngẫu nhiên của bạn là xấu. Nhiều cuộc gọi tiếp theo sẽ trả về cùng một vale. Bạn chỉ muốn đặt hạt giống của bạn một lần.

+0

Thực hiện cuộc gọi không tốt khi gọi điện thoạiSeed mỗi lần, giám sát ngu ngốc –

+0

Tôi thích ý tưởng này. Bạn có thể vui lòng xây dựng trên tùy chọn biểu thức tính toán (Tôi không quen với khái niệm này)? –

+0

Nó sẽ trông giống như: 'rnd { cho phép! a = GetNext 10; hãy để! b = GetExponential 2.5; hãy để! c = GetNormal; trả về a, b, c } ' – IngisKahn

3

Dưới đây là một giải pháp thread-safe tầm thường sử dụng System.Random, nếu đó là bất kỳ sự giúp đỡ:

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

Bạn thực sự có thể sử dụng nó: http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform. Để chuyển đổi từ các số ngẫu nhiên được tạo ra bởi 'System.Random' thành các số được phân phối bình thường. –

3

Nó tốt hơn để đặt tất cả m_w/m_z và có liên quan chức năng vào một lớp. Như thế này:

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

Sau đó, có ít nhất hai giải pháp: khởi chạy mỗi luồng với trường hợp riêng của đối tượng Random; hoặc sử dụng ThreadLocal<Random> lớp học cho cùng một điều - bảo đảm rằng mỗi thread có trường hợp riêng của lớp Random.

CHỈNH SỬA: Ngoài ra, MailboxProcessor với phương pháp PostAndReply là cách hay để chia sẻ một trình tạo đơn giữa các chuỗi. Không cần phải lo lắng về việc đồng bộ hóa.

+1

Vấn đề với việc tạo ra một cá thể mới mỗi lần là họ có nguy cơ nhận được cùng một hạt giống. Tôi nghĩ rằng điều này là khá có khả năng xảy ra trong một Array.Parallel.map (fun n -> let r = new Random() ...) kịch bản. –

+2

@FrancescoDeVittori: Bạn có thể kết hợp dữ liệu chủ đề cụ thể trong giá trị giống của bạn, như 'Thread.CurrentThread.ManagedThreadId'. – Daniel

+0

@FrancescoDeVittori Có, sử dụng 'DateTime' cho hạt giống ban đầu là một ý tưởng tồi. Nhưng bạn có thể sử dụng 'RNGCryptoServiceProvider.GetBytes' hoặc một cái gì đó tương tự cho nó. – qehgt