System.Random
中的浮点RNG看起来很简单,但对我来说却不准确:
instance Random Double where
randomR = randomRFloating
random rng =
case random rng of
(x,rng') ->
-- We use 53 bits of randomness corresponding to the 53 bit significand:
((fromIntegral (mask53 .&. (x::Int64)) :: Double)
/ fromIntegral twoto53, rng')
where
twoto53 = (2::Int64) ^ (53::Int64)
mask53 = twoto53 - 1
尽管此RNG确实可以统一生成FP编号,但我对此有疑问: RNG无法生成的范围内有一些编号。
具体来说,“太”精确的数字。例如,此RNG可以生成(表示为二进制IEEE双精度FP;先后是符号,指数和尾数):
0 01111111101 0000000000000000000000000000000000000000000000000000
正好是¼,但不能产生:
0 01111111101 0000000000000000000000000000000000000000000000000001
因为最后一个1
(很少)的精度太高。
我怀疑是否会发生这种情况,因此我编写了自己的统一FP RNG:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bifunctor
import System.Random
randomFloat1to2 :: (RandomGen g, Random a, RealFloat a) => g -> (a, g) -- Uniformly generates random Float among [1,2)
randomFloat1to2 g = first (1+) (random g)
randomFloatExp :: forall a g. (RandomGen g, Random a, RealFloat a) => Int -> g -> (a, g) -- Uniformly generates random Float among [0, 2^(exp+1))
randomFloatExp exp g = let
(minexp, _) = floatRange (0 :: a)
(upperHalf, g') = random g
in if exp == minexp
then (0, g') -- Denormal numbers treated as 0
else if upperHalf
then first (2^^exp *) (randomFloat1to2 g')
else randomFloatExp (exp-1) g'
randomFloat :: (RandomGen g, Random a, RealFloat a) => g -> (a, g) -- Uniformly generates random Float among [0,1)
randomFloat = randomFloatExp (-1)
说明:
在[0,1)范围内的Double
个数字中,[1 / 2,1)中的所有数字均具有IEEE指数01111111110
,而其他数字则具有较低的数字。因此,RNG进行硬币翻转:
如果出现头部,则RNG通过将½和[1,2)之间的随机数相乘来在[1 / 2,1)中选择一个随机数。由于默认的random
有效地选择了一个随机的尾数,因此我们可以向其添加1以使范围为[1,2)的RNG均匀。
如果不是,则RNG会通过[¼,½),[⅛,¼)等进行递归,直到范围不正常为止。
我的版本可以被认为是更好的版本吗?
答案 0 :(得分:1)
浮点RNG是否应该更精确地设置为0?
取决于功能目标以及@Eric Postpischil的评论。
这就像问tan(x)
是否比sin(x)
好:这取决于目标。两者对于较小的x
给出相同的答案,但在范围的另一部分有所不同。
我的版本可以被认为是更好的版本吗?
之所以不好的原因是:即使在最坏的情况下,完成工作的时间也可能非常长。
RNG通过[¼,½),[⅛,¼)等进行递归,直到范围不正常为止。
通过[¼,½),[⅛,¼)进行非正常递归可能需要进行数千次递归。
要帮助tad,代码可以生成一个随机指数,但仍然是递归的,但速度更快(除非随机数为0,否则n位random
的MSBit变为指数,然后需要递归调用。)
这将递归减少了N倍(random
位的位宽度)。
然而,解决方案不可能重复scores次,而需要使用不同的random
来计算指数。