问题
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