2016-05-18 25 views
3

私はSystem.Randomライブラリを使用しています。私は完全に理解していない動作を経験しました。以下のshuffle機能は、Fischer-Yates shuffleの実装であり、置換えなしでランダムサンプルとしても機能します。例えば。 2.ランダムジェネレータの予期しない動作

import   Control.Monad    as M 
import   Control.Monad.ST 
import   Data.Vector.Unboxed   as VU 
import   Data.Vector.Unboxed.Mutable as VUM 
import   System.Random 

go = do 
    g <- newStdGen 
    let (rand_vec1, g1) = randVector 10 g 
    let (rand_vec2, g2) = randVector 10 g 
    let (rand_sample1, g3) = shuffle rand_vec1 2 g 
    let (rand_sample2, g4) = shuffle rand_vec1 2 g 
    print rand_vec1 
    print rand_vec2 
    print rand_sample1 
    print rand_sample2 

randVector :: (RandomGen g) => Int -> g -> (VU.Vector Int, g) 
randVector n = shuffle vector (VU.length vector) where 
    vector = VU.enumFromN 0 n 

shuffle :: (RandomGen g, Unbox a) => VU.Vector a -> Int -> g -> (VU.Vector a, g) 
shuffle li size g = runST $ do 
    vector <- VU.unsafeThaw li 
    let n = VUM.length vector - 1 
    let step g i = do 
       let (j,g') = randomR (0,n) g 
       VUM.swap vector i j 
       return g' 
    g' <- M.foldM step g [0..size-1] 
    v' <- VU.unsafeFreeze vector 
    let vec = VU.take size v' 
    return (vec, g') 

リストおよびリストの長さとshuffleを呼び出すと、リスト全体をシャッフルしますが、リストおよび数2でそれを呼び出すと、長さのランダムなサンプルを抽出する必要があり、私はrand_vec1rand_vec2であることに気づきます同じ乱数発生器が使用されるので、常に同じであり、これは予想される。

しかし、両方とも同じランダムジェネレータを使用していても、rand_sample1rand_sample2は異なります。見知らぬ人でも、時間の半分以上は、常にではありませんが、rand_sample2は、(以下の例のように)サンプリングされるベクトルの2つの最初の番号を含んでいます。どうして? 出力例:

[3,0,4,9,7,2,1,8,5,6]

[3,0,4,9,7,2,1、 8,5,6]

[9,2]

[3,0]

(また、コードレビューが理解される)

答えて

2

ためはunsafeThaw/Freezeを使用しています。実際に入力ベクトルを変更しています。つまり、この場合はrand_vec1です。

がこれを実行してみてください:

go = do 
    g <- newStdGen 
    let (rand_vec1, g1) = randVector 10 g 
    print rand_vec1 
    let (rand_vec2, g2) = randVector 10 g 
    print rand_vec2 
    let (rand_sample1, g3) = shuffle rand_vec1 2 g 
    print rand_sample1 
    print ("rand_vec1: ", rand_vec1) 
    let (rand_sample2, g4) = shuffle rand_vec1 2 g 
    print rand_sample2 
    print ("rand_vec1: ", rand_vec1) 

そして、ここでは、出力されます。

*Main> go 
[7,0,3,5,2,6,9,8,1,4] 
[7,0,3,5,2,6,9,8,1,4] 
[0,3] 
("rand_vec1: ", [0,3,7,5,2,6,9,8,1,4]) 
[3,7] 
("rand_vec1: ", [3,7,0,5,2,6,9,8,1,4]) 

あなたの2番目の質問に答えるために、短い答えは、ベクターとしてshuffle株式同じメモリで返されるということです(変更された)入力ベクトルである。

+0

明示的に返されない限り、 'runST'の変更は"エスケープ "できないと思いました。 'shuffle'は副作用がありません。なぜなら、すべてが' runST'の内部で行われるからです。 – tsorn

+2

これが意味することの例を提示できれば助かります。ここでの問題は、STモナドの根底にある仮定に違反する安全でない操作を使用していることです。 – ErikR

+0

それはそれを説明する! 'VU.unsafeThaw'を' VU.thaw'に変更することで、ランダムサンプルは等しくなります。私が 'runST'で安全でない操作を使用しないと、操作に副作用があってはいけませんよね? – tsorn