使用System.Random中的mkStdGen生成随机布尔值

4
在以下程序中,为什么每一行都返回True?我原以为由于我使用不同的种子来初始化mkStdGen,一些行应该返回True,而其他行应该返回False
module Main where
import System.Random

main = do
  --why every single line prints "True" ?
  print $ fst (random (mkStdGen 1) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 2) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 3) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 4) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 5) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 6) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 7) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 8) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 9) :: (Bool, StdGen))
  print $ fst (random (mkStdGen 10) :: (Bool, StdGen))

即使在源代码mkStdGen中也指出:"不同的参数应该能够产生不同的生成器":
{- |
The function 'mkStdGen' provides an alternative way of producing an initial
generator, by mapping an 'Int' into a generator. Again, distinct arguments
should be likely to produce distinct generators.
-}
mkStdGen :: Int -> StdGen -- why not Integer ?
mkStdGen s = mkStdGen32 $ fromIntegral s

2
有趣的是,前53667个整数(Int)在第一次运行时会产生一个True值(length $ takeWhile id $ map fst . random . mkStdGen) [1..])。 - bheklilr
2个回答

3

这并不是一个答案,但我完全同意jberryman的观点,建议放弃前几个值或使用mwc-random。如果你这样做,会得到一种相当有趣的模式。

>>> :m +Control.Arrow +Data.List +System.Random
>>> let groups = group $ map (fst . random . mkStdGen) [0 .. maxBound] :: [Bool]
>>> map (head &&& length) groups
[(True,53668),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53669),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53669),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53668),(False,53669),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53669),
 (True,53668),(False,53668),(True,53669),(False,53668),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53669),(False,53668),(True,53668),(False,53669),(True,53668),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53669),(False,53668),
 (True,53668),(False,53669),(True,53668),(False,53668),(True,53668),(False,53669),
 ...

看起来这些值中存在着非常明显的规律。所有的组长度都是53668或53669,并且在我运行的时间内,它会在TrueFalse之间交替出现。

如果你忽略第一个值:

>>> let r :: StdGen -> (Bool, StdGen); r = random
>>> map (head &&& length) $ group $ map (fst . r . snd . r . mkStdGen) [1 .. maxBound]
[(False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,1),(True,2),(False,1),(True,1),(False,2),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,1),
 (False,2),(True,1),(False,1),(True,2),(False,1),(True,2),
 (False,1),(True,1),(False,2),(True,1),(False,1),(True,2),
 (False,1),(True,1),(False,2),(True,1),(False,1),(True,2),
 ...

这些值在TrueFalse之间交替出现只是因为group的工作方式如此,这并不需要引起警惕。相反,要注意每个组中的值的数量要小得多且更难预测,尽管我认为我仍然可以看到其中的模式(看看2的排列方式)。如果您要使用System.Random,最好在使用之前生成多个值,甚至最好在使用之前生成随机数量的值。

谢谢。我正在尝试使用System.Random,因为它在“learnyouahaskell”中有介绍。我会尝试一下mwc-randomtf-random(这两个库在jberryman引用的文章中有提到)。 - artella
1
嗨,似乎丢弃前几个生成器并不适用于所有情况。请参见https://dev59.com/fX3aa4cB1Zd3GeqPYyBt。 - artella

3

有趣的是,这在haskell reddit上刚刚出现(讨论在此处),并在此文章中有所说明。该文章建议从种子中丢弃第一个生成器,如下所示:

better_mkStdGen seed = snd $ randomR (1,6) $ mkStdGen seed

或者您可以使用一些声称具有分布特征的随机包,例如mwc-random


想一想,除非您的代码完全正确且具有“const True”的性能,否则可能永远不应该使用不承诺分布或加密属性的随机数生成器。 - jberryman
1
非常好,感谢您提供这篇文章(我之前没有看到过)和提示。它建议在文章中使用 tf-random - artella
嗨,似乎丢弃第一个生成器并不适用于所有情况。请参见https://dev59.com/fX3aa4cB1Zd3GeqPYyBt。 - artella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接