模拟一个从40个数字中选择6个数字的彩票,我想使用系统随机生成器在Haskell中创建一个数字列表,但要消除经常出现的重复。
如果我有以下条件:
import System.Random
main :: IO ()
main = do
rs <- forM [1..6] $ \_x -> randomRIO (1, 40) :: (IO Int)
print rs这已经是一半了。但是我如何过滤掉重复的内容呢?在我看来,我需要某种while循环来构造一个列表,过滤列表中已经存在的元素,直到列表达到所需的大小。如果我可以生成一个无限的随机数列表,并在IO monad中对其进行过滤,我相信这会起作用,但我不知道如何处理。虽然循环在Haskell中通常是被弃用的,但我不确定Haskeller在这里的真正方式。这是while循环的合法用例吗?如果是,该如何使用呢?
发布于 2015-01-01 21:11:30
您正在寻找的函数是来自Data.List的nub,用于过滤重复项。
import Data.List
import System.Random
main = do
g <- newStdGen
print . take 6 . nub $ (randomRs (1,40) g :: [Int])发布于 2015-01-01 16:45:34
如果你不介意使用库,那么安装random-shuffle package并像这样使用它:
import System.Random.Shuffle
import Control.Monad.Random
main1 = do
perm <- evalRandIO $ shuffleM [1..10]
print perm如果您想了解如何在Haskell中使用列表实现一个朴素的Fischer-Yates混洗,请看下面的代码:
shuffle2 xs g = go [] g (length xs) xs
where
go perm g n avail
| n == 0 = (perm,g)
| otherwise = let (i, g') = randomR (0,n-1) g
a = avail !! i
-- can also use splitAt to define avail':
avail' = take i avail ++ drop (i+1) avail
in go (a:perm) g' (n-1) avail'
main = do
perm <- evalRandIO $ liftRand $ shuffle2 [1..10]
print permgo帮助器函数的参数包括:
perm -构造的置换so farg -当前生成器valuen -可用itemsavail的长度-可用项-即尚未被选择为置换的一部分的项
go只是将avail中的一个随机元素添加到正在构造的排列中,并使用新的可用性列表和新的生成器递归地调用自己。
要仅从xs提取k随机元素,只需在k处启动go,而不是在length xs处
shuffle2 xs k g = go [] g k xs
...您还可以使用临时数组(在ST或IO monad中)来实现Fischer-Yates类型的算法。random-shuffle中的shuffleM函数使用了一种完全不同的方法,您可能会对此感兴趣。
更新:下面是一个在F-Y风格算法中使用ST数组的示例:
import Control.Monad.Random
import Data.Array.ST
import Control.Monad
import Control.Monad.ST (runST, ST)
shuffle3 :: RandomGen g => Int -> g -> ([Int], g)
shuffle3 n g0 = runST $ do
arr <- newListArray (1,n) [1..n] :: ST s (STUArray s Int Int)
let step g i = do let (j,g') = randomR (1,n) g
-- swap i and j
a <- readArray arr i
b <- readArray arr j
writeArray arr j a
writeArray arr i b
return g'
g' <- foldM step g0 [1..n]
perm <- getElems arr
return (perm, g')
main = do
perm <- evalRandIO $ liftRand $ shuffle3 20
print perm发布于 2015-01-01 11:04:25
我使用了C++中的Fisher Yates Shuffle和一个不错的随机数生成器,取得了巨大的成功。如果您愿意分配一个数组来保存数字1到40,这种方法是非常有效的。
https://stackoverflow.com/questions/27727980
复制相似问题