首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >无重复的随机数

无重复的随机数
EN

Stack Overflow用户
提问于 2015-01-01 10:53:11
回答 5查看 1.6K关注 0票数 3

模拟一个从40个数字中选择6个数字的彩票,我想使用系统随机生成器在Haskell中创建一个数字列表,但要消除经常出现的重复。

如果我有以下条件:

代码语言:javascript
运行
复制
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循环的合法用例吗?如果是,该如何使用呢?

EN

回答 5

Stack Overflow用户

发布于 2015-01-01 21:11:30

您正在寻找的函数是来自Data.Listnub,用于过滤重复项。

代码语言:javascript
运行
复制
import Data.List
import System.Random

main = do
    g <- newStdGen
    print . take 6 . nub $ (randomRs (1,40) g :: [Int])
票数 4
EN

Stack Overflow用户

发布于 2015-01-01 16:45:34

如果你不介意使用库,那么安装random-shuffle package并像这样使用它:

代码语言:javascript
运行
复制
import System.Random.Shuffle
import Control.Monad.Random

main1 = do
  perm <- evalRandIO $ shuffleM [1..10]
  print perm

如果您想了解如何在Haskell中使用列表实现一个朴素的Fischer-Yates混洗,请看下面的代码:

代码语言:javascript
运行
复制
  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 perm

go帮助器函数的参数包括:

  • perm -构造的置换so far
  • g -当前生成器value
  • n -可用items
  • avail的长度-可用项-即尚未被选择为置换

的一部分的项

go只是将avail中的一个随机元素添加到正在构造的排列中,并使用新的可用性列表和新的生成器递归地调用自己。

要仅从xs提取k随机元素,只需在k处启动go,而不是在length xs

代码语言:javascript
运行
复制
shuffle2 xs k g = go [] g k xs
  ...

您还可以使用临时数组(在ST或IO monad中)来实现Fischer-Yates类型的算法。random-shuffle中的shuffleM函数使用了一种完全不同的方法,您可能会对此感兴趣。

更新:下面是一个在F-Y风格算法中使用ST数组的示例:

代码语言:javascript
运行
复制
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
票数 3
EN

Stack Overflow用户

发布于 2015-01-01 11:04:25

我使用了C++中的Fisher Yates Shuffle和一个不错的随机数生成器,取得了巨大的成功。如果您愿意分配一个数组来保存数字1到40,这种方法是非常有效的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27727980

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档