我正在与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_vec1
和rand_vec2
总是相同的,这是预期的,因为使用了相同的随机数生成器。
但是,rand_sample1
和rand_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]
(此外,感谢代码审查)
答案 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
返回的向量与(修改的)输入向量共享相同的内存。