我正在尝试编写一个带有两个参数的函数,一个参数是随机整数应位于的范围的上限。另一个参数是要生成的随机数列表的数量。现在,我只是试图能够生成给定范围内的唯一随机数列表。
使用以下代码,我可以生成一个随机数字列表,但是,这些数字的类型为randomList 5 (0,10) :: IO [Int]
,而不是[Int]
。该代码还不能确保列表中不重复整数。
import Test.QuickCheck
t :: Int -> (Int,Int) -> Gen [Int]
t n r = vectorOf n (choose r)
randomList n r = head `fmap` (sample' $ t n r)
尽管我仍然找不到解决方案,但我已经查看了各种StackOverflow答案。许多答案只能使用System.Random
答案 0 :(得分:1)
您只需使用System.Random即可完成操作。
getStdRandom (randomR (0, 100))
但是您也可以使用QuickCheck来实现。您可以使用choose :: Random a => (a, a) -> Gen a
,它可以用于具有Random
实例的任何类型。您提供范围。或使用elements :: [a] -> Gen a
。它接受一个纯值列表(尚未在Gen
中),选择一个并在Gen
中返回它。
generate $ choose (0,100)
generate $ elements [0..100]
答案 1 :(得分:0)
我看过各种StackOverflow答案,尽管我仍然不能 找到解决方案。 许多答案只能使用System.Random生成具有相同种子的一个随机列表。
您总是可以重用随机数生成器的新状态来生成更多样本,除非您使用碰巧“丢球”的那些函数之一,否则将无法与实际的随机样本一起返回新状态
我建议使用Control.Monad.Random中定义的runRand
函数:
-- Run a random computation using the generator g, returning the result
-- and the updated generator.
runRand :: RandomGen g => Rand g a -> g -> (a, g)
此函数将单音 action 和生成器作为参数。
您提到您不想在输出列表中进行任何重复。这意味着您需要输入列表的随机置换(又称为 shuffle )的子集。 suitable page of the Haskell Wiki中有相应的代码。这个答案是从该代码中无耻地借来的,尤其是在页面结尾处显示“ 不替换的图形”的情况下,因此没有重复之处。
下面,我将以一个(桥牌/扑克)纸牌游戏为例,其输入列表为[0..51]
,该游戏模型用52张纸牌模拟了标准订购的纸牌。无疑,您不希望在输出列表中出现任何重复,因为在这种情况下,您手中的两位钻石之王会遭到严重皱眉。
下面的通用代码主要由2个功能组成:
功能swapElems
直接来自Wiki页面。它与随机性无关,其目的只是“执行”给定的排列。请注意,并非绝对需要为此目的使用数组。 System.Random.Shuffle中的代码将ad hoc树结构用于同一任务。
函数partShuffle
生成所需的单声道操作,并给出一个输入列表和一个样本大小(对于52张中的13张卡的常规过桥手为13张)。
import Data.List (sort, mapAccumL)
import Data.Tuple (swap)
import Text.Printf (printf)
import Data.Array
import Data.Array.MArray
import Data.Array.ST
import System.Random
import Control.Monad.Random
swapElems :: [a] -> [(Int, Int)] -> [a]
swapElems xs swaps = elems $ runSTArray (do
arr <- newListArray (0, maxIx) xs
mapM_ (swap arr) swaps
return arr)
where
maxIx = length xs - 1
swap arr (i,j) = do
vi <- readArray arr i
vj <- readArray arr j
writeArray arr i vj
writeArray arr j vi
partShuffle :: MonadRandom mr => [a] -> Int -> mr [a]
partShuffle xs sampleSize =
do
let maxIx = length xs - 1
ranStep i = do
j <- getRandomR (i, maxIx)
return (i, j)
swapList <- forM [0 .. (min (maxIx - 1) sampleSize)] ranStep
let xs2 = swapElems xs swapList
return $ take sampleSize xs2
在维基页面上没有提到函数stUnfold
,但是如果希望能够生成任意数量的样本,则可以自动执行将生成器状态从一个样本生成传递到下一个样本的任务。样本:
stUnfold :: (s -> (a,s)) -> s -> Int -> ([a], s)
stUnfold go s0 count = let auxList = replicate count ()
xgo state _ = swap $ go state
in swap $ mapAccumL xgo s0 auxList
签名中的类型 s 代表随机数生成器状态的类型。
λ> stUnfold (\n -> (n*n, n+1)) 0 10
([0,1,4,9,16,25,36,49,64,81],10)
λ>
我们现在可以使用这些功能来生成10张随机的“手牌”,每张13张牌。出于说明目的,前两个是通过手动传递生成器的状态来生成的,而后八个是使用stUnfold
实用程序功能一次生成的。
-- for prettyprinting purposes:
myShow :: [Int] -> String
myShow = concatMap (printf "%02d ")
main = do
let -- setup random number generator:
seed = 4242
rng0 = mkStdGen seed
-- standard bridge/poker card deck of 52 cards
-- 13 cards out of 52, no replacement, 10 samples:
itemCount = 52
deck = [0 .. (itemCount-1)]
sampleSize = 13
sampleCount = 10
-- type of action1 is: Rand StdGen [Int]
let action1 = partShuffle deck sampleSize
-- passing random generator manually:
let (sample1, rng1a) = runRand action1 rng0
putStrLn $ myShow $ sample1
let (sample2, rng1b) = runRand action1 rng1a
putStrLn $ myShow $ sample2
-- automated random generator passing:
let (samples, rng2) = stUnfold (runRand action1) rng1b (sampleCount - 2)
mapM_ (\ls -> putStrLn $ myShow ls) samples
$ cards
49 38 15 14 03 24 22 02 37 43 44 31 39
48 34 46 07 21 29 28 01 50 08 14 12 51
27 31 48 19 20 01 16 04 15 35 30 50 46
02 42 29 48 35 05 19 45 18 34 44 09 51
37 47 49 05 19 16 50 21 02 22 39 11 12
04 13 07 23 16 36 27 22 20 21 11 49 38
45 02 27 36 13 04 14 12 11 26 09 38 00
03 14 13 40 32 05 06 29 30 27 43 15 38
48 15 00 04 11 10 13 07 21 36 09 38 37
14 48 33 10 05 07 50 03 51 41 29 44 12
$
请注意,也可以使用一个返回N个样本的单声道动作。例如,Wiki页面中的grabble
函数会返回这种单调动作。
也有可能,一旦您有一个单音动作生成一个样本,就可以使用replicateM函数获得一个生成N个样本的动作。以下代码模糊了引擎盖下发生的事情,但确实产生了与上面完全相同的输出。
let action1 = partShuffle deck sampleSize
action10 = replicateM 10 action1
let (samples, rng3) = runRand action10 rng0
mapM_ (\ls -> putStrLn $ myShow ls) samples