top page > computer > haskell > web_lecture > for_programmer > randoms.html
更新日:
文責: 重城良国

疑似乱数列の生成

動機

モンテカルロ法での円周率の計算のために正方形内にランダムな点をプロットする。

Haskell98における疑似乱数

Haskell 98標準では疑似乱数生成ライブラリが定義されていた。実用上いくつかの問題があるためHaskell 2010では削除されている。Haskell Platformに含まれるrandomパッケージとして使える。

ライブラリの確認

システムにSystem.Randomライブラリがあることを確認する。

% ghci
Prelude> :m System.Random
Prelude System.Random>

エラーにならずプロンプトにSystem.Randomが追加されればライブラリはある。

予測可能で良い

一様に分布する数列ならば予測可能な数列で良い。返す数列を引数で指定する。x座標用とy座標用にふたつの数列を作る。

関数randoms

System.Randomライブラリにはrandoms関数がある。以下のように考えておこう。

randoms :: StdGen -> [Double]

StdGen型の値を与えるとDouble型の値のリストを返す。ランダムな値から成る無限リストを生成する。

StdGen型

StdGen型は「乱数の種」だ。この値によって異なった乱数列が生成される。値が同じならば同じ列が返る。StdGen型の値は関数mkStdGenでIntから生成できる。

mkStdGen :: Int -> StdGen

試してみる

% ghci
Prelude> :m System.Random
Prelude System.Random> mkStdGen 8
9 1
Prelude System.Random> :t it
it :: StdGen

mkStdGen 8によって9 1というStdGen型の値が得られる。疑似乱数列を作成する。

Prelude System.Random> take 3 . randoms $ mkStdGen 8 :: [Double]
[0.6022994713892826, 0.62331532360708, 0.9885723665310981]

型注釈:: [Double]が必要だ。randomsは多相関数だ。いろいろな型のランダム値を生成できる。学習ずみのパラメトリック多相ではなく型クラスのところで学習するアドホック多相だ。

関数randomRs

関数randomsでは生成される乱数列の範囲が指定できない。指定した範囲内の乱数列を生成するために関数randomRsがある。

randomRs :: (Double, Double) -> StdGen -> [Double]

第1引数のタプルで乱数の範囲を指定する。

Prelude System.Random> take 3 . randomRs (2, 4) $ mkStdGen 8 :: [Double]
[3.2045989427785653, 3.24663064721416, 3.9771447330621963]

2から4のあいだの乱数列が生成される。

関数split

座標の指定のためにはx, yに対応するふたつのランダムな値の系列が必要だ。関数splitで「ランダムの種」をふたつにわける。

split :: StdGen -> (StdGen, StdGen)

Prelude System.Random> split $ mkStdGen 8
(10 40692, 360126 2147483398)

正方形内のランダムな点

原点を中心とする1辺2の正方形内のランダムな点の列を生成する。驚かずに「そのまま」入力してみよう。すぐに説明する。

Prelude System.Random> :m + Control.Arrow
Prelude System.Random Control.Arrow> take 3 . uncurry zip . (randomRs (-1, 1) *** randomRs (-1, 1)) . split $ mkStdGen 8 :: [(Double, Double)]
[(-3.18859986651685e-2, -0.4475857685951523), (-0.4281869025678364, 0.969606436457163), (-0.7979778666086881, -0.6623840670853804)]

最後の:: [(Double, Double)]に注意しよう。型注釈をつけないとデフォルトでIntegerと解釈されてしまい-1, 0, 1だけになってしまう。

関数split

split $ mkStdGen 8

2つの乱数の種のタプルを返す。

演算子(***)

動作

演算子(***)は2要素タプルの両方の要素にそれぞれの関数を適用する関数だ。(* 2) *** (+ 3)は1要素目を2倍し2要素目に3を足す関数となる。

Prelude System.Random Control.Arrow> (* 2) *** (+ 3) $ (4, 2)
(8,5)

使用

(randomRs (-1, 1) *** randomRs (-1, 1)) . split $ mkStdGen 8

タプルの第1要素、第2要素の両方にrandomRs (-1, 1)を適用している。別々の系列の乱数列のタプルが返る。型は([Double], [Double])となる。

関数zip

動作

zipは2つのリストをとりタプルのリストにする。

zip :: [a] -> [b] -> [(a, b)]

Prelude System.Random Control.Arrow> zip [0 ..] "hello"
[(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]

uncurryによって引数である2つのリストをタプルにまとめる。uncurry zipは「リストのタプルをタプルのリストにする」関数となる。

uncurry zip :: ([a], [b]) -> [(a, b)]

Prelude System.Random Control.Arrow> :t zip
zip :: [a] -> [b] -> [(a, b)]
Prelude System.Random Control.Arrow> :t uncurry zip
uncurry zip :: ([a], [b]) -> [(a, b)]
Prelude System.Random Control.Arrow> uncurry zip ([0 ..], "hello")
[(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]

使用

uncurry zip . (randomRs (-1, 1) *** randomRs (-1, 1)) . split $ mkStdGen 8

x座標のリストとy座標のリストのタプルをx座標とy座標から成るタプルのリストとした。

まとめ

原点を中心とする1辺2の正方形内のランダムな点の列は以下のように生成される。

uncurry zip . (randomRs (-1, 1) *** randomRs (-1, 1)) . split $ mkStdGen 8

乱数の種のもととなる整数値8は適当な値だ。実際の関数の定義のときには仮引数とする。

「2種類の整数型」へもどる 「モンテカルロ法のコーディング」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics