Haskell函数生成随机数,使得每次数字与前一个数字不同

时间:2013-06-30 11:52:08

标签: haskell random

我想要一个随机生成的6位数字。保证我永远不会将此功能调用超过1000次。每次调用它时,该函数都应该能够返回不同的数字。

我想在没有任何参数的情况下将此函数称为nextRandom。 Haskell中有没有适合我的库?我不能保持种子。 Haskell可以将当前时间用作seed

更新:问题的背景。

我正在生成图形(点格式),我想确保所有顶点都有不同的标签。我可以将生成时间作为标签附加,但我通过生成随机数来实现这一点。

4 个答案:

答案 0 :(得分:7)

纯函数(如nextRandom,没有参数)就像数学函数。在每次调用时,他们使用相同的参数产生相同的结果。

所以你要求它是不可能的,因为

  • 您期望随机数字。
  • 您希望该函数具有某种内存,以便知道哪些数字已经生成。

只需使用haskell方式,将种子或随机生成器传递给函数,或使用monad。如果有帮助,您可以提前创建1000个数字,然后从列表中检索它们。

答案 1 :(得分:6)

您不能拥有每次都返回不同数字的纯函数。它的输出取决于它以前的调用,而不仅仅依赖于它的参数。但是你可以创建一个带有到目前为止生成的数字集的monad,然后重试生成一个随机数,直到它找到一个到目前为止尚未生成的数字。对于以下示例,我使用了MonadRandom包。

import Control.Monad
import Control.Monad.Random
import Control.Monad.State
import Data.IntSet (IntSet)
import qualified Data.IntSet as IS
import System.Random (getStdGen)

type RandDistinct g a = StateT IntSet (Rand g) a

evalDistinct :: RandomGen g => RandDistinct g a -> g -> a
evalDistinct k = evalRand (evalStateT k IS.empty)

上述类型使用StateT来增强随机数生成器,以便记住到目前为止生成的数字集。当我们想要评估这个monad中的计算时,我们从一个空集开始,用evalRand计算内部计算。

现在我们可以编写一个每次都返回一个不同数字的函数:

nextDistinct :: RandomGen g => (Int,Int) -> RandDistinct g Int
nextDistinct range = loop
  where
    -- Loop until we find a number not in the set
    loop = do
        set <- get
        r <- getRandomR range
        if IS.member r set
          then loop -- repeat
          else put (IS.insert r set) >> return r

并测试它是如何工作的:

main = getStdGen >>= print . evalDistinct (replicateM 50 $ nextDistinct (10, 99))

请注意nextDistinct使用简单策略 - 如果集合中已存在新数字,则重试生成新数字。只要碰撞次数很少,这种方法就可以正常工作。

答案 2 :(得分:1)

这里可能有助于更多地使用您要询问的函数的上下文。我远非专家,但是这样的事情会帮助你吗?

import Control.Monad.Random

rnd :: (RandomGen g) => Rand g Int
rnd = getRandomR (100000,999999)

main = do
  putStr "\nPlease type 'nextRandom' or 'quit':\n> "
  mainLoop [] where
    mainLoop memory = do
      command <- getLine
      case command of
        "nextRandom" -> do next <- randomLoop memory
                           putStr (show next ++ "\n> ")
                           mainLoop (next:memory)
        "quit"       -> return ()
        otherwise    -> putStr "\n> "
    randomLoop memory = do  
      value <- evalRandIO rnd
      if elem value memory 
         then randomLoop memory
         else return value

答案 3 :(得分:1)

不完全是您正在寻找的,但如果您只需要一个通用的采样 - 无需更换例程,您可以使用类似this example module之类的东西,我在SO上提出另一个问题。

只需提前生成许多不同的六位数字,并在任何地方使用它们。例如:

import System.Random.MWC
import Sample

main :: IO ()
main = do
  ns <- withSystemRandom . asGenIO $ \gen -> sample [100000..999999] 10 gen
  print ns

-- [754056,765889,795475,389702,120426,740641,556446,490338,534738,213852]