【问题标题】:Trim an infinite list of random numbers修剪一个无限的随机数列表
【发布时间】:2020-02-11 12:25:15
【问题描述】:

我正在尝试修剪一个随机数列表,以便生成的较小列表中 [0,1] 中的数字总和累积到小于 1 的值。

这很有趣,因为这些列表前缀长度的平均值是esomehow

在获取前缀长度时遇到了一个问题 - 我设法让程序在确定的无限列表或随机列表切片上工作,但程序挂在无限随机列表上。我做错了什么?

import System.Random

-- Count list items that accumulate to 1.
count :: (Num a, Ord a) => [a] -> Int
count xs = 1 + length xs' 
         where xs'= takeWhile (< 1) $ scanl1 (+) xs

-- Works of infinite list 
a = (return (repeat 0.015)) :: IO [Double]
fa = fmap count a 
--67 

-- Works on trimmed list of randoms
rio = randomRIO (0::Double, 1)         
b = sequence $ take 10 (repeat rio)
fb = fmap count b 
-- fb is usually 2 to 5

-- Hangs on infinite list of randoms
c = sequence (repeat rio)
fc = fmap count c
-- fc hangs... ;(

【问题讨论】:

  • sequence (repeat rio) 本身挂起,因为它必须在产生任何信息之前完成无限多个 IO 操作。 IO 很严格。
  • @luqui 也许更值得注意的是take 10 . sequence $ repeat rio 也挂了。 Evgeny 很可能认为sequence $ repeat rio 成功返回了一个惰性列表,并且只是试图强制它实际上挂起,就像repeat 0“挂起”从未完成评估一样。
  • 从概念上讲,IO [Double] 是一个指令列表,或一个命令式程序,用于计算[Double]。正如@luqui 所说,sequence (repeat rio) 是一个无限程序,它会永远重复调用rio。如果不需要,惰性将使您不必自己计算无限的指令列表,但如果您想要其结果,它不会使您不必执行整个程序。由于程序是无限的,执行永远不会终止,结果也永远不会出现。
  • @malloy - 事实上,根据所有详细的 cmets,我认为一旦 a、b 和 c 的类型签名相同,它们的“惰性级别”应该是相同的,但事实并非如此提供。

标签: list haskell random


【解决方案1】:

您可以定义一个 IO 操作来创建无限范围的随机数流,如下所示:

import System.Random

randomRIOs :: (Random a) => (a, a) -> IO [a]
randomRIOs (a, b) = randomRs (a, b) <$> newStdGen

之后,以下内容适用于您对count 的定义:

main = do
    n <- count <$> randomRIOs (0::Double, 1)
    print n

【讨论】:

    【解决方案2】:

    你不可能有一个无限的随机数列表,因为随机性太严格了。因此,您不能在拨打count 之外拨打sequence。您可以尝试的一件显而易见的事情是在count 本身内部部分重新实现sequence

    count :: (Num a, Ord a, Monad m) => [m a] -> m (Maybe Int)
    count = go 0 0
      where go n total [] = pure Nothing
            go n total (x:xs) = do
              num <- x
              let total' = total + num
                  n' = succ n
              if total' >= 1
                then pure $ Just n'
                else go n' total' xs
    

    我还修改了结果以返回一个 Maybe,因为为空列表返回 1 似乎是错误的(就像你的那样),或者返回一个列表的长度,即使它的元素总和小于 1。

    另一个合理的改变是不接受[m a],而只接受一个m a,它可以可靠地产生任意次数的值。那么我们就不用担心输入用完了,所以也不需要 Maybe:

    count :: (Num a, Ord a, Monad m) => m a -> m Int
    count m = go 0 0
      where go n total = do
              num <- m
              let total' = total + num
                  n' = succ n
              if total' >= 1
                then pure n'
                else go n' total'
    

    然后您可以简单地调用count $ randomRIO (0::Double, 1),它会根据需要生成任意数量的随机数。

    【讨论】:

    • “随机性太严格”是什么意思?
    猜你喜欢
    • 2015-10-15
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    相关资源
    最近更新 更多