Máy tính xác định và không thể tạo số ngẫu nhiên. Thay vào đó, chúng dựa vào các công thức toán học trả về phân phối các số trông ngẫu nhiên.Chúng được gọi là các trình tạo số giả ngẫu nhiên. Tuy nhiên, do tính quyết định, chúng ta có vấn đề là nếu chúng ta chạy các công thức này theo cách tương tự trong mỗi lần gọi chương trình của chúng ta, chúng ta sẽ nhận được cùng một trình tạo số ngẫu nhiên. Rõ ràng, điều này là không tốt, vì chúng tôi muốn số của chúng tôi là ngẫu nhiên! Vì vậy, chúng ta phải cung cấp cho trình tạo ngẫu nhiên giá trị hạt giống ban đầu thay đổi từ chạy đến chạy. Đối với hầu hết mọi người (tức là, những người không làm công cụ mã hóa), trình tạo số ngẫu nhiên được gieo vào thời điểm hiện tại. Trong Haskell, trình tạo giả ngẫu nhiên này được thể hiện bằng loại StdGen
. Hàm mkStdGen
được sử dụng để tạo trình tạo số ngẫu nhiên với một hạt giống. Không giống như C, nơi có một trình tạo số ngẫu nhiên toàn cục, trong Haskell, bạn có thể có bao nhiêu tùy thích và bạn có thể tạo chúng bằng các hạt giống khác nhau. Tuy nhiên, có một thông báo trước: vì các con số là giả ngẫu nhiên, không có gì đảm bảo rằng các trình tạo số ngẫu nhiên được tạo ra với các số hạt giống khác nhau trả về ngẫu nhiên so với số khác. Điều này có nghĩa rằng khi bạn gọi randomBool
và cung cấp cho nó giá trị hạt giống liên tiếp, không có gì đảm bảo rằng số bạn nhận được từ số StdGen
bạn tạo là ngẫu nhiên so với số StdGen
được ghép với người kế thừa. Đây là lý do tại sao bạn nhận được gần 50000 True
của.
Để nhận dữ liệu thực sự trông ngẫu nhiên, bạn cần tiếp tục sử dụng cùng trình tạo số ngẫu nhiên. Nếu bạn thấy, hàm random
Haskell có loại StdGen -> (a, StdGen)
. Bởi vì Haskell là tinh khiết, chức năng random
có một bộ tạo số ngẫu nhiên, tạo ra một giá trị giả ngẫu nhiên (phần tử đầu tiên của giá trị trả về) và sau đó trả về một số mới StdGen
đại diện cho bộ tạo hạt giống với hạt giống ban đầu, nhưng sẵn sàng cho số ngẫu nhiên mới. Bạn cần giữ lại số StdGen
khác xung quanh và chuyển nó đến hàm random
tiếp theo để nhận dữ liệu ngẫu nhiên.
Dưới đây là ví dụ, tạo ba bool ngẫu nhiên, a
, b
và c
.
randomBools :: StdGen -> (Bool, Bool, Bool)
randomBools gen = let (a, gen') = random gen
(b, gen'') = random gen''
(c, gen''') = random gen'''
in (a, b, c)
Lưu ý cách biến số gen
được "luồn" thông qua các cuộc gọi đến ngẫu nhiên.
Bạn có thể đơn giản hóa trạng thái vượt qua bằng cách sử dụng đơn vị trạng thái. Ví dụ,
import Control.Monad.State
import System.Random
type MyRandomMonad a = State StdGen a
myRandom :: Random a => MyRandomMonad a
myRandom = do
gen <- get -- Get the StdGen state from the monad
let (nextValue, gen') = random gen -- Generate the number, and keep the new StdGen
put gen' -- Update the StdGen in the monad so subsequent calls generate new random numbers
return nextValue
Bây giờ bạn có thể viết các randomBools
chức năng như:
randomBools' :: StdGen -> (Bool, Bool, Bool)
randomBools' gen = fst $ runState doGenerate gen
where doGenerate = do
a <- myRandom
b <- myRandom
c <- myRandom
return (a, b, c)
Nếu bạn muốn tạo ra một (hữu hạn) danh sách các Bool
s, bạn có thể làm
randomBoolList :: StdGen -> Int -> ([Bool], StdGen)
randomBoolList gen length = runState (replicateM length myRandom) gen
Chú ý cách chúng ta trả về StdGen
làm phần tử thứ hai của cặp được trả về, để cho phép nó được gán cho các hàm mới.
Đơn giản hơn, nếu bạn chỉ muốn tạo danh sách vô hạn các giá trị ngẫu nhiên cùng loại từ StdGen
, bạn có thể sử dụng hàm randoms
. Điều này có chữ ký (RandomGen g, Random a) => g -> [a]
. Để tạo danh sách vô hạn gồm Bool
sử dụng hạt giống bắt đầu là x
, bạn chỉ cần chạy randoms (mkStdGen x)
. Bạn có thể triển khai ví dụ của mình bằng cách sử dụng length $ takeWhile id (randoms (mkStdGen x))
. Bạn nên xác minh rằng bạn nhận được các giá trị khác nhau cho các giá trị ban đầu khác nhau của x
, nhưng luôn có cùng giá trị nếu bạn cung cấp cùng một số x
.
Cuối cùng, nếu bạn không quan tâm đến việc bị ràng buộc với đơn độc IO
, Haskell cũng cung cấp trình tạo số ngẫu nhiên toàn cầu, giống như ngôn ngữ mệnh lệnh. Gọi hàm randomIO
trong đơn vị IO
sẽ cung cấp cho bạn một giá trị ngẫu nhiên của bất kỳ loại nào bạn thích (miễn là nó là một thể hiện của Random
typeclass, ít nhất). Bạn có thể sử dụng điều này tương tự như myRandom
ở trên, ngoại trừ trong đơn IO
. Điều này có thêm sự tiện lợi rằng nó được tiền hạt giống trước thời gian chạy Haskell, có nghĩa là bạn thậm chí không phải lo lắng về việc tạo ra một StdGen
. Vì vậy, để tạo ra một danh sách ngẫu nhiên của 10 Bool
s trong IO
đơn nguyên, tất cả các bạn phải làm là replicateM 10 randomIO :: IO [Bool].
Hope this helps :)
Xây dựng về vấn đề này: máy phát điện ngẫu nhiên không được bảo đảm để có một phân phối ngẫu nhiên trên các giá trị hạt giống, nhưng nó được đảm bảo phân phối ngẫu nhiên qua các chuỗi giá trị được tạo ra. – mange