3

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

我想在nextRandom没有任何参数的情况下调用这个函数。Haskell 中是否有适合我的图书馆?我无法维持一颗种子。Haskell 可以使用当前时间作为seed.

更新:问题的背景。

我正在生成一个图形(点格式),我想确保所有顶点都有不同的标签。我可以通过将生成时间作为标签来做到这一点,但我被这个想法所困扰,因为这样做是通过生成随机数来实现的。

4

4 回答 4

7

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

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

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

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

于 2013-06-30T11:56:10.227 回答
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使用一个简单的策略 - 如果集合中已经存在一个新数字,则重试生成一个新数字。只要冲突的数量很少,这种方法就可以正常工作。

于 2013-06-30T15:05:11.170 回答
1

不完全是您正在寻找的东西,但如果您只需要一个通用的无替换采样例程,您可以使用类似这个示例模块的东西,我为另一个关于 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]
于 2013-06-30T21:18:21.073 回答
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
于 2013-06-30T14:03:11.143 回答