The Haskell 98 Report
top | back | next | contents | function index


27  乱数


module Random (
RandomGen(next, split, genRange),
StdGen, mkStdGen,
Random( random,   randomR, 
randoms,  randomRs,
randomIO, randomRIO ),
getStdRandom, getStdGen, setStdGen, newStdGen
  ) where

---------------- The RandomGen class ------------------------

class RandomGen g where
  genRange :: g -> (Int, Int)
  next     :: g -> (Int, g)
  split    :: g -> (g, g)

---------------- A standard instance of RandomGen -----------
data StdGen = ... -- Abstract

instance RandomGen StdGen where ...
instance Read     StdGen where ...
instance Show     StdGen where ...

mkStdGen :: Int -> StdGen

---------------- The Random class ---------------------------
class Random a where
   randomR :: RandomGen g => (a, a) -> g -> (a, g)
   random  :: RandomGen g => g -> (a, g)

   randomRs :: RandomGen g => (a, a) -> g -> [a]
   randoms  :: RandomGen g => g -> [a]

   randomRIO :: (a,a) -> IO a
   randomIO  :: IO a

instance Random Int     where ...
instance Random Integer where ...
instance Random Float   where ...
instance Random Double  where ...
instance Random Bool    where ...
instance Random Char    where ...

---------------- The global random generator ----------------
newStdGen    :: IO StdGen
setStdGen    :: StdGen -> IO ()
getStdGen    :: IO StdGen
getStdRandom :: (StdGen -> (a, StdGen)) -> IO a


Random ライブラリは擬似乱数生成を扱う。このライブラリはでは、 繰り返し同じ結果を得られるよになっている。これは指定の初期乱数生成器 を使う。動作ごとに違う結果がほしいときにはシステム初期化生成器を使う か、外部からシードを与えればよい。

このライブラリは2層に分れている。

27.1  RandomGen クラスと StdGen 生成器

RandomGen クラスは乱数生成器への共通インタフェースを供給する。

  class RandomGen g where
    genRange :: g -> (Int,Int)
    next     :: g  -> (Int, g)
    split    :: g -> (g, g)
  
    -- Default method
    genRange g = (minBound,maxBound)

Random ライブラリは、RandomGen クラスのインスタンス、 抽象データタイプ StdGen を提供する。

  data StdGen = ... -- Abstract
  
  instance RandomGen StdGen where ...
  instance Read      StdGen where ...
  instance Show      StdGen where ...
  
  mkStdGen :: Int -> StdGen

RandomGen クラスのインスタンス StdGen は少なくとも 30ビットの genRange

next を繰り返し使用した結果は少なくとも、[2,3] の "Minimal Standard Random Number Generator" にある統計的頑健性を有して いなければならない。split の実装について詳細に知らない場合に するべきことは、split が (a) 同一ではない (b) 上記の意 味で独立にロバストな生成器を届けるようにすることである。

StdGenShow/Read インスタンスにより乱数 生成器の状態を保持する方法が提供されている。 read (show g) == g でなければならない。

さらに read は任意の文字列(show により生成されたも のである必要はない)を StdGen 型の値へ写像するのに使える。一 般的に StdGenRead インスタンスは以下の性質をも つ。

関数 mkStdGen は、Int から生成器への写像により、初 期生成器をつくるもうひとつの方法を提供している。ここでも、変数が異れ ば異った生成器がつくられるように見える。

もちろん、プログラマは独自の RandomGen のインスタンスを提供 することができる。

実装上の警告。一見魅力的な split の実装は、

  instance RandomGen MyGen where
    ...
    split g = (g, variantOf g)

である。ここでは、split 自身を返し、新たな 生成器を g から導出する。しかし、この二つは外見上独立した 生成器をと考えられる。

  g1 = snd (split g)
  g2 = snd (split (fst (split g)))

もし、split が真に独立した生成器を(指定どおり)配分すると、g1g2 は独立でなければならない。しかし、実際にはこのふたつは variantOf g に等しい。上記のような実装は仕様には合わない。

27.2  Random クラス

手で乱数を提供するソースがあるので、プログラマは色々な型の乱数の値を Random クラスからとりだすことができる。

class Random a where
   randomR :: RandomGen g => (a, a) -> g -> (a, g)
   random  :: RandomGen g => g -> (a, g)

   randomRs :: RandomGen g => (a, a) -> g -> [a]
   randoms  :: RandomGen g => g -> [a]

   randomRIO :: (a,a) -> IO a
   randomIO :: IO a

     -- Default methods
   randoms g = x : randoms g' 
   where 
     (x,g') = random g
   randomRs = ...similar...

   randomIO        = getStdRandom random
   randomRIO range = getStdRandom (randomR range)


instance Random Int     where ...
instance Random Integer where ...
instance Random Float   where ...
instance Random Double  where ...
instance Random Bool    where ...
instance Random Char    where ...

27.3  グローバル乱数生成器

StdGen 型の暗黙のグローバル乱数生成器というものが一つだけあっ て、IO モナドにより或る種のグローバル変数に保持されている。 これはシステム依存のなんらかの方法により時刻と日付あるいは Linux カー ネルの乱数生成器により、自動的に初期化される。決定論的動作をのぞむな らsetStdGen を使用すること。

  setStdGen    :: StdGen -> IO ()
  getStdGen    :: IO StdGen
  newStdGen    :: IO StdGen
  getStdRandom :: (StdGen -> (a, StdGen)) -> IO a

参考文献

[1]

FW Burton and RL Page, "Distributed random number generation", Journal of Functional Programming, 2(2):203-212, April 1992.

[2]

SK Park, and KW Miller, "Random number generators - good ones are hard to find", Comm ACM 31(10), Oct 1988, pp1192-1201.

[3]

DG Carta, "Two fast implementations of the minimal standard random number generator", Comm ACM, 33(1), Jan 1990, pp87-88.

[4]

P Hellekalek, "Don't trust parallel Monte Carlo", ACM SIGSIM Simulation Digest 28(1), pp82-89, July 1998.

The Web site http://random.mat.sbg.ac.at/ is a great source of information.


The Haskell 98 Report
top | back | next | contents | function index
December 2002