提问人:artella 提问时间:3/2/2014 最后编辑:Communityartella 更新时间:4/24/2014 访问量:169
使用 System.Random 时的时间相关性(使用 System.Random.TF 时不存在)
Temporal correlations when employing System.Random (not present when employing System.Random.TF)
问:
这个问题涉及人们从连续的种子生成连续随机数时观察到的时间相关性的起源(其中为每个种子丢弃相同数量的生成器)。System.Random
在使用 System.Random 的 mkStdGen 生成随机布尔值答案 1 和使用 System.Random 的 mkStdGen 生成随机布尔值答案 2 中,有人建议(基于引用他们的 reddit 文章)应该丢弃前几个生成器以获得合理的结果。然而,我发现,无论丢弃多少生成器,当人们查看分布的时间方面时,如果使用连续的种子生成连续的随机数(每个种子丢弃相同数量的生成器),就会获得不良结果。
我的问题是,所采用的算法以所描述的方式导致不同种子之间的这种时间相关性是什么?System.Random
如果我们生成一个无限序列的随机布尔值,那么获得具有相同值的连续布尔值(例如 in )的概率为 。作为
快速检查我们有归一化条件:P(n)
n
[True,True,True]
[False,True,True,True,False]
(1/2)^n
P(1)+P(2)+....P(infty) = (1/2) + (1/2)^2 + ... = 1
代码如下:
module Main where
import Data.List
import System.Random
generateNthGenerator startGen 0 = startGen
generateNthGenerator startGen n = generateNthGenerator newGen (n-1)
where newGen = snd $ ((random startGen) :: (Bool,StdGen))
better_mkStdGen generation seed =
generateNthGenerator (mkStdGen seed) generation
randomNums generation =
map (fst . random . (better_mkStdGen generation)) [0 .. maxBound] :: [Bool]
-- e.g. [True,True,False,False,False,True,True,True,False,False]
sortedLengthOfConsecutives num randList =
sort $ map length $ take num $ group randList
frequencyOfConsecutives sortedLengthOfCons =
map (\x -> (head x, length x)) $ group sortedLengthOfCons
results = frequencyOfConsecutives
$ sortedLengthOfConsecutives 10000
$ randomNums 10
main = do
print results -- [(8,1493),(9,8507)]
使用连续种子的生成器生成每个连续的布尔值,在使用生成的随机结果之前丢弃 10 个生成器。生成一个由 10000 个随机数组成的序列,因此我们预计大约 5000 个布尔值后面跟着相反的布尔值(例如 in ),表示有 2500 个布尔值,后面跟着相同的布尔值,但后面跟着相反的布尔值(例如 in),大约有 1250 个布尔值,分为 3 个,依此类推。[True]
[False,True,False,False]
[True,True]
[False,True,True,False]
因此,从上面的代码中,我们得到了 1493 个 8 组和 8507 个 9 组。这不是预期的,无论丢弃了多少生成器,我们都会得到类似的结果(在上面的示例中,每个种子丢弃的生成器数量为 10 个)。[请注意,当我们进行相同的实验时,我们没有得到相同的行为,请参阅后面]。tf-random
如果我们使用先前生成的生成器生成连续的布尔值(我猜这是最初设计使用的方式,因为它本身返回一个新的生成器),我们似乎会得到更合理的结果:random
module Main where
import Data.List
import System.Random
generateRandomInner gen =
let (randBool, newGen) = (random gen)::(Bool,StdGen)
in randBool:(generateRandomInner newGen)
generateRandoms seed =
let (randBool, newGen) = (random $ mkStdGen seed)::(Bool,StdGen)
in randBool:(generateRandomInner newGen)
seed = 0
randomNums = generateRandoms seed
sortedLengthOfConsecutives num randList =
sort $ map length $ take num $ group randList
frequencyOfConsecutives sortedLengthOfCons =
map (\x -> (head x, length x)) $ group sortedLengthOfCons
results = frequencyOfConsecutives
$ sortedLengthOfConsecutives 10000
$ randomNums
main = do
print results
--[(1,4935),(2,2513),(3,1273),(4,663),(5,308),
-- (6,141),(7,86),(8,45),(9,16),(10,12),(11,6),
-- (12,1),(13,1)]
因此,我们得到 4935 个单例(大致等于 0.5*10000)、2513 个双元组(大致等于 0.5^2*10000)、1273 个三元组(大致等于 0.5^3*10000)等,这是我们预期的。
因此,如果我们(通过)生成一个随机序列,其中每个连续的随机数都是与连续的种子一起生成的,其中我们为每个种子丢弃相同数量的生成器,则无论丢弃的生成器数量如何,不良的相关性仍然存在。System.Random
导致这种情况的随机数生成的算法属性是什么?System.Random
请注意,如果我们使用上面的失败方法(redditt文章),那么我们会得到预期的结果:tf-random
module Main where
import Data.List
import System.Random
import System.Random.TF
generateNthGenerator startGen 0 = startGen
generateNthGenerator startGen n = generateNthGenerator newGen (n-1)
where newGen = snd $ ((random startGen) :: (Bool,TFGen))
better_mkStdGen generation seed =
generateNthGenerator (seedTFGen (0,0,0,seed)) generation
randomNums generation =
map (fst . random . (better_mkStdGen generation)) [0 .. maxBound] :: [Bool]
-- e.g. [True,True,False,False,False,True,True,True,False,False]
sortedLengthOfConsecutives num randList =
sort $ map length $ take num $ group randList
frequencyOfConsecutives sortedLengthOfCons =
map (\x -> (head x, length x)) $ group sortedLengthOfCons
results = frequencyOfConsecutives
$ sortedLengthOfConsecutives 10000
$ randomNums 10
main = do
print results
-- [(1,4867),(2,2573),(3,1243),(4,646),(5,329),
-- (6,176),(7,80),(8,43),(9,26),(10,10),(11,4),
-- (12,2),(19,1)]
即 50% 是单例,25% 是双组(2 人一组),等等......
答:
让我们先看看代码说了什么,我们就可以到达那里了。
首先,应用于 等同于:random
Bool
randomB :: StdGen -> (Bool, StdGen)
randomB g = let (i, g') = next g in (i `mod` 2 == 1, g')
事实上,如果我用你的程序中合适的位置替换它,我会得到相同的结果。关键是,在确定布尔值时,我们关心的只是下一个值是偶数还是奇数。random
randomB
Int
接下来,我们来看一下定义:StdGen
data StdGen
= StdGen Int32 Int32
所以两个 s 是状态。让我们看看它们是如何初始化的,以及它们是如何调整的:Int32
mkStdGen
next
mkStdGen :: Int -> StdGen -- why not Integer ?
mkStdGen s = mkStdGen32 $ fromIntegral s
mkStdGen32 :: Int32 -> StdGen
mkStdGen32 s
| s < 0 = mkStdGen32 (-s)
| otherwise = StdGen (s1+1) (s2+1)
where
(q, s1) = s `divMod` 2147483562
s2 = q `mod` 2147483398
...
stdNext :: StdGen -> (Int, StdGen)
-- Returns values in the range stdRange
stdNext (StdGen s1 s2) = (fromIntegral z', StdGen s1'' s2'')
where z' = if z < 1 then z + 2147483562 else z
z = s1'' - s2''
k = s1 `quot` 53668
s1' = 40014 * (s1 - k * 53668) - k * 12211
s1'' = if s1' < 0 then s1' + 2147483563 else s1'
k' = s2 `quot` 52774
s2' = 40692 * (s2 - k' * 52774) - k' * 3791
s2'' = if s2' < 0 then s2' + 2147483399 else s2'
请注意两件有趣的事情:
初始化方式保证它将是 1,除非你发送一个非常高的数字到 ,在这种情况下,它将是 2(在初始化为 2 的范围内有少于 200 个值。
s2
mkStdGen
Int32
s2
状态的两半是分开的——更新只取决于前一个,而不依赖于前一个,反之亦然。
s2
s2
s1
因此,如果你检查从传递给的某个固定数量的代数中得到的生成器,它们状态的后半部分将始终是相同的。better_mkStdGen
尝试将以下内容添加到您的程序中:
print $ map (dropWhile (/= ' ') . show . better_mkStdGen 10) [0 .. 20]
那么问题来了,为什么混音功能无法正确混音最后一点。请注意,它的编写方式将与 具有相同的奇偶校验,因此如果最终小于零,则状态仅与前一个状态具有不同的奇偶校验。s1
s1'
k
s1
s1
s1
s1'
在这一点上,我需要稍微挥手说,计算方式意味着如果两个初始值 for 彼此接近,则两个值 for 也将靠近,并且通常只会相距 40014 倍于它们最初的距离,这在我们允许的范围内使相邻值很可能最终位于零的同一侧。s1'
s1
s1'
s1
评论