【问题标题】:Generate a random integer in a range in Haskell without a seed在没有种子的Haskell中生成一个范围内的随机整数
【发布时间】:2011-12-07 13:50:29
【问题描述】:

如何在不使用任何种子的情况下从范围 (a, b) 中生成 Haskell 中的随机数?

函数应该返回一个 Int 而不是 IO Int。 我有一个函数 X,它接受和 Int 和其他参数并输出不是 IO 的东西。

如果这不可能,我如何使用 Time 库生成种子并使用 mkStdGen 生成范围内的随机数?

任何帮助将不胜感激。

【问题讨论】:

  • stackoverflow.com/a/2738824/570689 - 类似问题的好答案
  • 听起来你可以期待的最好的结果是randomInt lo hi = lo 或类似的东西。如果没有某种种子或 IO,函数每次都必须返回相同的值。并且下限与任何其他Int 一样随机。 :)
  • “任何考虑产生随机数字的算术方法的人,当然都处于犯罪状态。”归功于 John Von Neumann in Knuth, D. 计算机编程的艺术,第 2 卷,第 3 页。 1(1981)。
  • 我相信在没有 IO 或种子值的情况下在 Haskell 中获取随机数的最佳方法是使用 a function like this

标签: haskell random


【解决方案1】:

一个函数不能在没有IO的情况下返回Int,除非它是一个纯函数,即给定相同的输入,您将始终得到相同的输出。这意味着,如果您想要一个没有IO 的随机数,则需要将种子作为参数。

使用random 库:

  • 如果你选择一个种子,它的类型应该是StdGen,你可以使用randomR从中生成一个数字。使用newStdGen 创建一个新种子(这必须在IO 中完成)。

     > import System.Random
     > g <- newStdGen
     > randomR (1, 10) g
     (1,1012529354 2147442707)
    

    randomR 的结果是一个元组,其中第一个元素是随机值,第二个元素是用于生成更多值的新种子。

  • 否则,您可以使用randomRIO 直接在IO monad 中获取随机数,并为您处理所有StdGen 的东西:

     > import System.Random
     > randomRIO (1, 10)
     6
    

【讨论】:

  • 对于那些这样做的人,不要忘记import System.Random
【解决方案2】:

如果不采取各种不安全的做法,这样的函数不可能具有类型 Int 而不是类型 IO Int 或类似的东西。 Int 类型的函数(或者,在这种情况下为常量)是纯函数,这意味着每次“调用”函数(检索常量的值)时,都保证“返回”相同的值。

如果您想在每次调用时返回一个不同的、随机选择的值,您将需要使用 IO-monad。

在某些情况下,您可能希望为整个程序生成一个随机生成的值,即,从程序的角度来看,它的行为就好像它是一个纯值。每次在程序的同一运行中查询值时,都会返回相同的值。由于整个程序本质上是一个IO-action,因此您可以生成该值一次并传递它,但这可能会感觉有点笨拙。有人可能会争辩说,在这种情况下,将值与 Int 类型的顶级常量相关联并使用 unsafePerformIO 构造该常量仍然是安全的:

import System.IO.Unsafe  -- be careful!                                         
import System.Random

-- a randomly chosen, program-scoped constant from the range [0 .. 9]            
c :: Int
c = unsafePerformIO (getStdRandom (randomR (0, 9)))

【讨论】:

【解决方案3】:
fmap yourFunctionX $ randomRIO (a, b)

fmap (\x -> yourFunctionX aParam x anotherParam) $ randomRIO (a, b)

结果将是IO whateverYourFunctionXReturns类型。

如果你import Control.Applicative,你可以说

yourFunctionX <$> randomRIO (a, b)

(\x -> yourFunctionX aParam x anotherParam) <$> randomRIO (a, b)

你可能会发现更清楚

【讨论】:

    【解决方案4】:

    请注意,您可以使用 IO monad 获得无限的随机值列表,并在非 IO 函数中使用 [Int]。这样你就不必随身携带种子,但当然仍然需要携带清单。幸运的是,有很多列表处理函数可以简化这种线程,在复杂的情况下你仍然可以使用State monad。

    还请注意,您可以轻松地将 IO Int 转换为 Int。如果 foo 产生一个 IO Int,而 bar 将一个 Int 作为它的唯一参数并返回一个非 IO 值,则如下所示:

    foo >>= return . bar
    

    或使用 do 表示法:

    do 
        a <- foo
        return $ bar a
    

    或者使用fmap(单子是函子,&lt;$&gt;fmap的中缀版本):

    bar <$> foo
    

    【讨论】:

      【解决方案5】:

      我为此使用了 SipHash

      import Data.ByteArray.Hash
      import Data.ByteString (pack, cons)
      import Data.Word (Word8, Word64)
      
      random :: Word64 -> Word64 -> [Word8] -> Double
      random a b cs = (subtract 1) . (/(2**63)) . read . drop 8 . show $ sipHash (SipKey a b) (pack cs)
      

      read and drop 8 和 show 用于删除一个不支持(或者在我实现它时不支持)任何类型转换的新类型

      现在你想要一个范围内的 Int。整数更容易:

      random :: Word64 -> Word64 -> [Word8] -> (Integer, Integer) -> Integer
      random a b cs (low,high) = let
          span = high-low
          rand = read . drop 8 . show $ sipHash (SipKey a b) (pack cs)
          in (rand `mod` span) + low
      

      当然,对于相同的参数,你仍然会每次都得到相同的数字,所以你需要改变它们,即你仍然传递参数,只是没有返回值。这是否比 monad 更方便取决于(就我的目的而言)

      这就是我确保参数(特别是 [Word8] 参数)总是不同的方式:

      foo bytes = doSomethingRandom bytes
      bar bytes = map (\i -> foo (i:bytes)) [1..n]
      baz bytes = doSomething (foo (0:bytes)) (bar (1:bytes))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-08
        • 2014-08-11
        • 1970-01-01
        • 2010-09-22
        相关资源
        最近更新 更多