2013-06-30 71 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] 
相關問題