random
特徴
Haskell 98標準に準拠したライブラリ。
つぎのような特徴があります。
* 周期が短い
* 生成が遅い
このような問題があるため、Haskell 2010では、標準としては含まれていません。
パッケージrandomとして使うことができます。
使用法
乱数の種を取得する
乱数の種を取得するには、整数を変換して種をつくる方法と、
システムが生成した種を取り出すという方法とがあります。
整数値から乱数の種をつくる
Int型の整数から乱数の種をつくるには関数mkStdGenを使います。
code:haskell
-- random
:module System.Random
mkStdGen 8
9 1
mkStdGen 123
124 1
関数mkStdGenの型は、つぎのようになります。
code:haskell
mkStdGen :: Int -> StdGen
乱数の種を特定の整数からつくることで、
つねにおなじ並びとなる乱数列を取得することができます。
起動のたびに異なる乱数の種を取得する
起動のたびに異なる乱数の種を取得するにはIO値getStdGenを使います。
code:haskell
-- random
:module System.Random
getStdGen
852161251 1
システムによって、起動のたびに異なるように初期化されたランダムの種が
取得できます。
getStdGenはIO値です。
対話環境では入力された値が、通常の値であれば、評価してその値を表示します。
IO値であれば、それが表すアクションを実行して、結果として得られる値を表示します。
IO値getStdGenの型は、つぎのようになります。
code:haskell
getStdGen :: IO StdGen
種から乱数を得る
乱数の種から乱数を得るには関数randomを使います。
code:haskell
-- random
:module System.Random
g = mkStdGen 8
random g
(-398575370259562870,1380072070 2103410263)
関数randomは乱数値と新しい種をかえします。
乱数値は、デフォルトではInteger型の値となります。
型を指定することもできます。
code:haskell
random g :: (Double, StdGen)
(0.7492446889336446,1380072070 2103410263)
random g :: (Bool, StdGen)
(True,360126 40692)
random g :: (Char, StdGen)
('\319433',360126 40692)
新しい種から、つぎの乱数値を作ることができます。
code:haskell
random g :: (Char, StdGen)
('\319433',360126 40692)
random $ snd it :: (Char, StdGen)
('\282363',1525180386 1655838864)
このようにして、ひとつの種から、
つぎつぎに新しい乱数値を生成していくことができます。
無限乱数列を取得する
乱数をひとつずつ作っていくかわりに、生成されるすべての乱数を含む、
無限数列を作ることができます。
このような無限リストを作るには関数randomsを使います。
code:haskell
-- random
:module System.Random
g = mkStdGen 8
take 100 $ randoms g
[-398575370259562870,-6370604356117182359,8399777519602674086,...
IO内で簡単に乱数を取得する
乱数の種を更新しながら、
乱数をひとつずつ取り出すには、つぎのように実行します。
code:haskell
-- random
:module System.Random
getStdGen
551091070 1
random it :: (Int, StdGen)
(7702268877985608548,1221636208 2103410263)
setStdGen $ snd it
getStdGen
1221636208 2103410263
random it :: (Int, StdGen)
(6127740995264203513,8356773621 1780294415)
setStdGen $ snd it
IO値getStdGenで現在、システムに保存されている乱数の種を取得し、
関数randomでランダムな値と、新しい乱数の種を計算します。
そして、関数setStdGenを使って、新しい乱数の種をシステムに保存します。
このような動きをまとめたのがIO値randomIOです。
code:haskell
randomIO :: IO Int
7202189668116739859
randomIO :: IO Int
8741390758431768255
IO値randomIOは、手軽に乱数値を取得できる便利な値です。
乱数の範囲を指定する
ここまで説明してきた関数やIO値を使ったやりかたでは、
得られる乱数値の範囲を指定することができません。
範囲を指定して乱数値を取得するためには、
関数randomR、randomRs、randomRIOを使います。
乱数の範囲を指定して、種から乱数を得る
乱数の範囲を指定して、種から乱数を得るには、関数randomRを使います。
code:haskell
-- random
:module System.Random
g = mkStdGen 6
g
7 1
randomR (3, 7) g
(3,280098 40692)
randomR (3, 7) $ snd it
(7,470423557 1655838864)
randomR (3, 7) $ snd it
(4,834780103 2103410263)
関数randomRは、第1引数に、もとめる乱数値の下限と上限とからなる
タプルをとること以外は、関数randomと、おなじです。
得られる乱数値の範囲は、あたえた下限や上限の値を含みます。
乱数の範囲を指定して、無限乱数列を取得する
乱数の範囲を指定して、無限乱数列を取得するには、関数randomRsを使います。
code:haskell
-- random
:module System.Random
g = mkStdGen 6
g
7 1
take 10 $ randomRs (3, 7) g
乱数の範囲を指定して、IO内で簡単に乱数を取得する
乱数の範囲を指定して、IO内で簡単に乱数を取得するには、関数randomRIOを使います。
code:haskell
-- random
:module System.Random
randomRIO (3, 7)
3
randomRIO (3, 7)
4
randomRIO (3, 7)
7
randomRIO (3, 7)
7
乱数の種をふたつにわける
ふたつの系列の乱数値がほしいことがあります。
たとえば、平面上のランダムな点のリストが必要なとき、などです。
そのようなときには、関数splitを使って、乱数の種をふたつにわけます。
code:haskell
-- random
:module System.Random
g = mkStdGen 8
g
9 1
(g', g'') = split g
g'
10 40692
g''
360126 2147483398
take 10 $ randomRs (1, 6) g'
take 10 $ randomRs (1, 6) g''
システムから新しい乱数の種を取り出す
たとえば、つぎのようにして、乱数列を取り出してみる。
code:haskell
-- random
:module System.Random
g <- getStdGen
take 10 $ randomRs (1, 6) g
g' <- getStdGen
take 10 $ randomRs (1, 6) g'
ひとつめの乱数列と、ふたつめの乱数列とは、まったく、おなじものになる。
もちろん、これはこれで、正当な動作だ。
しかし、2回目に取り出す乱数列が、
1回目のものと異なるものであってほしいこともある。
そのようなときには、関数splitを使えばいい。
code:haskell
g0 <- getStdGen
(g, g1) = split g0
setStdGen g1
take 10 $ randomRs (1, 6) g
g2 <- getStdGen
(g', g3) = split g2
take 10 $ randomRs (1, 6) g'
つぎような流れとなる。
1. 乱数の種を取得する
2. 乱数の種を、ふたつにわける
3. そのうちの、ひとつを乱数列をもとめるのに使う
4. もうひとつは、システムに保存する
このようにすることで、毎回、新しい乱数の種が手に入る。
この流れをまとめた関数newStdGenがある
(ただし、並行実行でも、問題が生じないような仕組みがあるため、
上記とまったくおなじ動作というわけではない)。
code:haskell
g <- newStdGen
g
1102913371 40691
g' <- newStdGen
g'
1102953385 1655838863
関数newStdGenを使うことで、つねに、新しい乱数の種を、手に入れることができる。