2013-06-30 24 views
3

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

我想不带任何参数地调用此函数,如nextRandom。 Haskell中有没有适合我的图书馆?我无法维持一颗种子。 Haskell可以使用当前时间作为seed

更新:问题的上下文。

我正在生成一个图形(点格式),我想确保所有的顶点都有不同的标签。我可以通过附加生成时间作为标签来做到这一点,但是我通过生成随机数字而被这个想法咬了一口。

+5

“因此,我可以期望函数每次都返回不同的数字” - 野生假设。如果您只是生成随机数字,则有重复的机会。事实上,这个概率比你的直觉所说的要糟糕。检查生日悖论。 –

+0

@KarolyHorvath谢谢。修正了语言。 – Dilawar

+0

Karoly,虽然你说的是真的,但我不喜欢使用“悖论”这个词,因为它不是不合逻辑的或类似的东西:P就像说“蒙蒂霍尔悖论”,我更喜欢“蒙蒂霍尔问题”。 – Wes

回答

7

纯函数(如nextRandom,没有参数)就像数学函数一样。在每次调用时,它们都会以相同的参数产生相同的结果。

是因为

  • 你想到的随机数是什么你问这是不可能的。
  • 您希望函数具有某种内存来知道哪些数字已经生成。

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

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 
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使用一个简单的策略 - 重试生成新数字,如果它已经存在于集合中。只要碰撞次数很少,这就可以正常工作。

1

不完全是你在找什么,但如果你只是需要一个通用的取样 - 无替换例程,你可以使用类似于this example module的东西,我为此提出了另一个问题。

只需生成您需要的许多不同的六位数字,然后在任何需要的地方使用它们。例如:

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] 
相关问题