2013-01-07 6 views
13

乱数を生成するために私のアプリケーションでSystem.RandomRandomの型クラスを使用しています。しかし、私は新しい発電機を得るための制約がなければrandoms :: StdGen -> Int -> ([Float], StdGen)ランダムな値のリストを生成し、新しいジェネレータを取得する

のような機能で、任意の長さのランダムなfloatのリストを生成したいのですが、私は簡単に randoms gen n = (take n $ randoms gen) :: [Float]

を書くことができしかし、これは同じで私を残し私が始めたランダムなジェネレータは、この関数を2回連続して実行すると、別の場所に移動して新しいものを取得するまで、同じリストを取得することを意味します。

乱数ジェネレータを「リフレッシュ」しながら、無限(または任意の長さ)の乱数値リストを生成するにはどうすればよいですか。

+1

もう1つのオプションは、ジェネレータを2つに分割することです(http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:split)。その後、生成されたジェネレータの1つを使用して 'randoms'を呼び出し、もう一方のジェネレータを続行することができます。無限リストでも動作します。 – hammar

答えて

28

さて、あなたが持っている機能を見てみましょう:

random :: StdGen -> (Float, StdGen) -- From System.Random 

私たちは、ステートフルな計算を取得するStateモナドでこれをラップすることができます:

state :: (s -> (a, s)) -> State s a -- From Control.Monad.Trans.State 

random' :: State StdGen Float 
random' = state random 

を、我々は束を生成することができます浮動小数点数をそのまま使用するreplicateM

replicateM :: (Monad m) => Int -> m a -> m [a] -- From Control.Monad 

randoms' :: Int -> State StdGen [Float] 
randoms' n = replicateM n random' 

最後に、あなたはあなたが得る一つの関数定義にこれらのすべてを組み合わせた場合

randoms :: Int -> StdGen -> ([Float], StdGen) 
randoms n = runState (randoms' n) 

:つまり

randoms :: Int -> StdGen -> ([Float], StdGen) 
randoms n = runState (replicateM n (state random)) 

、私たちのようにプロセスを記述することができ、明示的な発電機の通過を取り戻すためState

  • ラップrandomStateモナド
  • それを複製n
  • モナドは、このような重要な概念である理由これは

それをアンラップ。まずモナドインターフェースのレンズを通して見ると、単純な計算である傾向があります。 Data.List(およびData.Tupleからswap)からmapAccumLを使用してStatesplitなし

3

代替、:

nRandoms n gen = mapAccumL(\g _ -> swap $ random g) gen [1..n] 

私は、これはいずれにも良いはず理由のための説得力を持っていないと言っているのに方法。

+0

標準ライブラリで 'swap'ですか? –

+0

@BenMillwood 'Data.Tuple'。最近追加されたが、私はどれくらい最近正確に覚えていない。見た目:ghc-7.0で最初に登場しました*。それほど最近ではありません。 –

5

ガブリエルの答えは正しいですが、これはMonadRandomパッケージがどのように実装されているか(ランダムなジェネレータでパラメータ化された状態のMonad)です。

毎回それを定義するのが節約され、Monadトランスフォーマーも付属しているので、他のMonadをランダムな値を生成できるものに変換することができます。

(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g) 

StdGenがRandomGenのインスタンスであることを起こるあなたは、単にそれをプラグインして行くことができますので、:として

あなたの例では、容易に実現することができます!

1

もっと一般的ですが、あなたが望むものと一致するタイプの関数を定義することができます。

import System.Random 

randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g) 
randoms' g n = 
    let (g1, g2) = split g 
    in (take n $ randoms g1, g2) 

split

split :: g -> (g, g)

を使用していてもsplit操作は、一つは、2つの別個の乱数発生器を得ることを可能にします。これは関数プログラム(例えば、乱数ジェネレータを再帰呼び出しに渡す)で非常に便利ですが、統計的に頑強な実装のsplit&hellipについてはほとんど行われていません。

それでもあなたが望むことはしません。 (私は簡単視覚的な比較のために以下の実施例にBoolを使用。)

ghci> g <- getStdGen 
ghci> randoms' g 5 :: ([Bool], StdGen) 
([False,False,False,True,False],1648254783 2147483398) 
ghci> randoms' g 5 :: ([Bool], StdGen) 
([False,False,False,True,False],1648254783 2147483398) 

メモランダムの配列が同じであること。

機能は発電機を分割する手間になりますが、速やかに廃棄します。代わりに、あなたのコードはIOモナドで実行されている場合、あなたは

のように、最後にグローバル乱数生成器を置き換えるために setStdGenを使用することができます

ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen) 
ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen) 
ghci> (a1,a2) 
([False,False,False,True,False],[True,True,True,False,True] 

のように後続の呼び出しにそれを通すことによってg2の使用を作ります

myAction :: Int -> IO ([Float],[Float]) 
myAction n = do 
    g <- getStdGen 
    let (f1,g2) = randoms' g n 
    let (f2,g3) = randoms' g2 n 
    setStdGen g3 
    return (f1, f2) 

周囲のスレッド状態が扱いにくく、エラーを起こしやすい。多数の定型文がある場合は、StateまたはSTを使用することを検討してください。

関連する問題