Random numbers in haskell. And shuffling a list

强颜欢笑 提交于 2021-02-10 14:50:22

问题


i am trying to write a function that when given a list would return a lsit in random order.

this is how i thought of doing it(the list is of length 52): generate random number between 1 and 52 take that element of the list.

a = [1,2,3..] !! getRandom 52

then recursively call same function.. generate random number between 1 and 51 and call on list with first element we picked removed.

(delete a [1,2,3..]) !! getRandom 51

And so on..after puting all elements picked in a list we get same list but shuffled. This is my random number function:

getRandom :: Int -> IO Int
getRandom x = getStdRandom (randomR (1,x))

But because this function returns IO Int not Int I can't use it to take random element from a list. What can i do here?

on a university task i need to shuffle a deck of 52 cards. We didn't trough Monads and advanced stuff yet. So, is there an easy way to take a list of 52 cards and shuffle it?


回答1:


In haskell you cannot create a random number out of thin air. You can create it from a seed, but then you get the excat same sequence of random numbers each time you use the same seed.

Or you take the seed from the outside world. Then you can "enter" a different seed each time you run the program, or you let a library pick one from the system time or however it does it - either way you are in IO-land. If you go this route, then you pick your random number inside an IO-operation and shuffle the list with it. The shuffling itself will be a pure operation. You can then print the shuffled list to IO, but you cannot escape IO-land.

If the focus of this task is to learn how to shuffle lists, then it probably doesn't matter much how you get your random number, as long as you get the shuffling right (which is tricky enough).

So write a function

shuffle :: Int->[a]->[a]

where the first parameter is a random seed. You can then stay in pure-land and use the System.Random functions you create more random numbers if you need any. Don't be disappointed if your program shuffles the list in the exact same way whenever you call it.




回答2:


You don’t really need the IO monad to generate random numbers, as you can create a random number generator by supplying a seed to function mkStdGen which is part of the System.Random module. And that way, your computation is reproducible, just by providing the same seed.

Import System.Random
let rg1     = mkStdGen seed
let randils = randoms rg1 :: [Double] -- infinite list of random values

According to the Rosetta web site, the core of the Knuth algorithm goes like this, with N permutable items numbered from 0 to N-1: https://www.rosettacode.org/wiki/Knuth_shuffle

for i from (N-1) down to 1  do:
    let j = random integer in range 0 <=j <=i
    swap items[i] with items[j]

So this being Functional Programming, we have a slight problem with the mutable array. There are two Haskell sample codes on Rosetta. The first one just uses random access into ordinary Haskell lists, thus accepting poor efficiency. The second one uses monadic stuff with the Data.Sequence module.

A possible compromise is the code below, which generates a random list of swap operations prior to applying them, using a fold, to a regular Data.Map asssociative array.

The critical function is randomPerm which takes a list of random Double values as its main parameter.

The simplistic main function generates just one sample. On my system, the output is « [5,2,8,3,0,4,6,1,7] ». You can generalize and get an unlimited supply of random permutations with an expression such as : « L.map (randomPerm itemCount) (chunksOf (itemCount-1)  randils)»

    -- Haskell code to generate random permutations of N items,
    -- using language-provided random numbers generation facilities.

    -- Knuth's shuffle  algorithm  for a zero-based array of size N:
    -- https://www.rosettacode.org/wiki/Knuth_shuffle


import  Data.List as L
import  Data.Map  as M
import  System.Random
type MI = M.Map Int Int  -- auxiliary associative array type


-- main loop of Knuth's algorithm:
makeSwapPairList :: Int -> [Double] -> [(Int,Int)]
makeSwapPairList itemCount rands = 
    let ls = reverse $ enumFromTo 1 (itemCount-1)
    in  L.map makeSwapPair (zip ls rands)

makeSwapPair :: (Int,Double) -> (Int,Int)
makeSwapPair (p, randX) = (p, floor ((fromIntegral (p+1))*randX))

-- generates a map where integers below itemCount are identically mapped
makeTrivialMap :: Int -> MI
makeTrivialMap itemCount = 
  let  ls1 = enumFromTo 0 (itemCount-1)
  in   M.fromList (zip ls1 ls1)

-- apply just one swap operation to an MI map:
applySwap :: MI -> (Int,Int) -> MI
applySwap ma p =
    let (i,j)=p ; mi = ma ! i ; mj = ma ! j
    in  M.insert i mj  (M.insert j mi ma)

-- apply a list of swap operations to an MI map:
applySwapList :: MI -> [(Int,Int)] -> MI
applySwapList ma pl = L.foldl' applySwap ma pl

-- returns a random permutation as a list of Int values:
randomPerm :: Int -> [Double] -> [Int]
randomPerm itemCount rands =
    let ma0 = makeTrivialMap itemCount
        ma1 = applySwapList ma0 (makeSwapPairList itemCount rands)
        pls1 = M.toAscList ma1
    in L.map snd pls1


-- simplified main program, for just one random permutation 
main =
  do
    let seed = 4242
    let itemCount = 9  -- should become 52

    -- random number generation in the [0,1( interval:
    let rg1     = mkStdGen seed
    let randils = randoms rg1 :: [Double] -- infinite list of random values
    let rands1  = L.take (itemCount-1) randils -- just enough for one sample

    let myRandomPerm = randomPerm itemCount -- function currying
    let perm1 = myRandomPerm rands1
    putStrLn (show perm1)



回答3:


Use mapM here. Try:

main = do 
    lst <- mapM getRandom [1..100]
    print lst

mapM is like map but for monadic operations (like IO).



来源:https://stackoverflow.com/questions/33723097/random-numbers-in-haskell-and-shuffling-a-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!