随机生成器意外行为

时间:2016-05-18 10:25:08

标签: haskell random random-sample

我正在与System.Random图书馆合作,并且我遇到了一些我不完全理解的行为。下面的shuffle函数是Fischer-Yates shuffle的一个实现,它也可以作为随机样本而无需替换。例如。使用列表调用shuffle并且列表的长度将对整个列表进行洗牌,但是使用列表调用它并且数字2应该提取长度为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')

我注意到rand_vec1rand_vec2总是相同的,这是预期的,因为使用了相同的随机数生成器。

但是,rand_sample1rand_sample2不同,即使它们都使用相同的随机生成器。更奇怪的是,超过一半的时间,但并非总是如此,rand_sample2只包含从中采样的两个第一个数字(如下例所示)。怎么会? 示例输出:

  

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

     

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

     

[9,2]

     

[3,0]

(此外,感谢代码审查)

1 个答案:

答案 0 :(得分:2)

由于shuffle正在使用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])

要回答第二个问题,简短的回答是shuffle返回的向量与(修改的)输入向量共享相同的内存。