Computers are deterministic and can't generate random numbers. Rather, they rely on mathematical formulas that return a distribution of numbers that look random. These are called pseudo-random number generators. However, because of the determinism, we have the problem that if we ran these formulas the same way during each invocation of our program, we would get the same random number generators. Obviously, this is no good, since we want our numbers to be random! Thus, we have to provide the random generator an initial seed value that changes from run-to-run. For most people (i.e., those not doing cryptographical stuff), the random number generator is seeded by the current time. In Haskell, this pseudo-random generator is represented by the StdGen
type. The mkStdGen
関数は、シードを持つ乱数ジェネレーターを作成するために使用されます。グローバルな乱数ジェネレーターが 1 つある C とは異なり、Haskell では、いくつでも持つことができ、異なるシードを使用してそれらを作成できます。
ただし、注意点があります。数値は疑似乱数であるため、異なるシードで作成された乱数ジェネレーターが、他のシードと比較してランダムに見える数値を返すという保証はありません。これは、呼び出して連続するシード値を指定した場合、作成した から取得した数値が、後続のシード値と比較してランダムrandomBool
であるという保証がないことを意味します。これが、ほぼ 50000 を取得する理由です。StdGen
StdGen
True
実際にランダムに見えるデータを取得するには、同じ乱数ジェネレーターを引き続き使用する必要があります。お気付きのように、random
Haskell 関数には type がありStdGen -> (a, StdGen)
ます。Haskell は純粋であるため、このrandom
関数は乱数ジェネレータを取り、疑似乱数 (戻り値の最初の要素) をStdGen
生成してから、元のシードでシードされたジェネレータを表す new を返しますが、新しい乱数を与える準備ができています。番号。ランダムなデータを取得するには、これを保持して次の関数にStdGen
渡す必要があります。random
a
、b
、およびの 3 つのランダムなブール値を生成する例を次に示し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)
random への呼び出しを通じて、変数がどのようにgen
「スレッド化」されるかに注目してください。
You can simplify passing state by using a state monad. For example,
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
Now you can write the randomBools
function as:
randomBools' :: StdGen -> (Bool, Bool, Bool)
randomBools' gen = fst $ runState doGenerate gen
where doGenerate = do
a <- myRandom
b <- myRandom
c <- myRandom
return (a, b, c)
If you want to generate a (finite) list of Bool
s, you can do
randomBoolList :: StdGen -> Int -> ([Bool], StdGen)
randomBoolList gen length = runState (replicateM length myRandom) gen
Notice how we return the StdGen
as the second element of the returned pair, to allow it to be given to new functions.
More simply, if you just want to generate an infinite list of random values of the same type from an StdGen
, you can use the randoms
function. This has the signature (RandomGen g, Random a) => g -> [a]
. To generate an infinite list of Bool
using a starting seed of x
, you simply run randoms (mkStdGen x)
. You can implement your example using length $ takeWhile id (randoms (mkStdGen x))
. You should verify that you get different values for different initial values of x
, but always the same value if you supply the same x
.
最後に、モナドに縛られることを気にしないのであればIO
、Haskell は命令型言語によく似たグローバルな乱数ジェネレーターも提供します。モナドで関数randomIO
を呼び出すIO
と、好きな型のランダムな値が得られます (Random
少なくとも型クラスのインスタンスである限り)。モナドmyRandom
を除いて、これは上記と同様に使用できます。IO
これには、Haskell ランタイムによって事前にシードされているという便利さが追加されています。つまり、StdGen
. Bool
したがって、モナドで 10のランダムなリストを作成するには、次のIO
ようにします。replicateM 10 randomIO :: IO [Bool].
お役に立てれば :)