Is there a library routine for random permutations? I didn't find any and did a quick hack, which works fine for my application (length of list < 100), but what would be a more elegant way?
permute :: StdGen -> [a] -> [a] permute gen [] = [] permute gen xs = (head tl) : permute gen' (hd ++ tail tl) where (idx, gen') = randomR (0,length xs - 1) gen (hd, tl) = splitAt idx xs
Markus -- Markus Schnell, Infineon Technologies AG
Is there a library routine for random permutations?
I didn't find any and did a quick hack, which works fine for my application (length of list < 100), but what would be a more elegant way?
permute :: StdGen -> [a] -> [a] permute gen [] = [] permute gen xs = (head tl) : permute gen' (hd ++ tail tl) where (idx, gen') = randomR (0,length xs - 1) gen (hd, tl) = splitAt idx xs
I've attached some *old* code that generates a random permutation. Hope it still works ... Cheers, Ralf ---- %-------------------------------= -------------------------------------------- \section{Generate a random permutation} %-------------------------------= --------------------------------------------
module RandomPerm( randomPerm, randomPerms) where import Random import Int import Array import ST
% - - - - - - - - - - - - - - - = - - - - - - - - - - - - - - - - - - - - - - - \subsection{Signature} % - - - - - - - - - - - - - - - = - - - - - - - - - - - - - - - - - - - - - - -
randomPerm :: (RandomGen g) => [a] -> g -> ([a], g) randomPerms :: (RandomGen g) => [a] -> g -> [[a]]
% - - - - - - - - - - - - - - - = - - - - - - - - - - - - - - - - - - - - - - - \subsection{Implementation} % - - - - - - - - - - - - - - - = - - - - - - - - - - - - - - - - - - - - - - - For a list of length |n| we calculate a random number between |0| and |n! - 1|. This random number is converted to the factorial number system, see \cite[p.66]{Knu97Art2}. Recall that in this system a sequence of `digits' $b_{n-1}\ldots b_0$ with $0 \leq b_i \leq i$ denotes the number $\sum_{i} b_i\cdot i!$ (note that $b_0$ is necessarily $0$). This sequence is then used as a recipe for building a random permutation: we exchange the elements at positions $i$ and $i + b_i$ for $0 \leq i < n$.
randomPerm as g = (permute num as, g') where (num, g') = generate (length as) g
randomPerms as g = as' : randomPerms as g' where (as', g') = randomPerm as g
Generates a random number between |0| and |n! - 1| in the `factorial' number system representation.
generate :: (RandomGen g) => Int -> g -> ([Int], g) generate n g = (convert fs r, g') where (f : fs) = reverse (take (n + 1) (factorials 0 1)) (r, g') = randomR (0, f - 1) g
Convert a number to a mixed-radix numeral (the radices are given as the first argument).
convert :: [Integer] -> Integer -> [Int] convert [] _n = [] -- |_n| should be |0| convert (f : fs) n = toInt q : convert fs r where (q, r) = divMod n f
The list of factorial numbers.
factorials :: Int -> Integer -> [Integer] factorials i f = f : factorials (i + 1) (f * fromInt (i + 1))
Note that we have to call |factorial i f| such that |f| is |i!|. The function |permute| permutes the given list according to the `factorial' numeral.
permute :: [Int] -> [a] -> [a] permute num as = runST ( do { a <- newSTArray bs undefined; sequence_ [ writeSTArray a i e | (i, e) <- zip is as ]; sequence [ swap a i (i + r) | (i, r) <- zip is num ]; mapM (readSTArray a) is }) where bs = (0, length as - 1) is = range bs
The operation |swap| exchanges two elements of a mutable array.
swap :: (Ix ix) => STArray s ix a -> ix -> ix -> ST s () swap a i j = do { x <- readSTArray a i; y <- readSTArray a j; writeSTArray a i y; writeSTArray a j x }
Is there a library routine for random permutations?
I didn't find any and did a quick hack, which works fine for my application (length of list < 100), but what would be a more elegant way?
Well, sorting is a special case of permuting, so my idea was to use the library routine List.sortBy :: (a -> a -> Ordering) -> [a] -> [a] passing it a comparison function which ignores its arguments and simply returns a random bit when invoked, e.g. permute = sortBy $ \_ _ -> if random() then GT else LT Unfortunately, this one-line hack is impossible since random numbers rely on state and sortBy is not prepared for use with a monad. Back in the old days when we had assignments... --Andreas -- Andreas Abel --<>-- What if Jesus is right? Theoretical Computer Science, University of Munich http://www.tcs.informatik.uni-muenchen.de/~abel/
Andreas Abel wrote:
Well, sorting is a special case of permuting, so my idea was to use the library routine
List.sortBy :: (a -> a -> Ordering) -> [a] -> [a]
passing it a comparison function which ignores its arguments and simply returns a random bit when invoked, e.g.
permute = sortBy $ \_ _ -> if random() then GT else LT
Unfortunately, this one-line hack is impossible since random numbers rely on state and sortBy is not prepared for use with a monad.
Well, even if it were possible to use sortBy with a monad, wouldn't your hack be a bit dangerous? Here is what the report has to say on sortBy: "... when the "By" function replaces an Ord context by a binary predicate, the predicate is assumed to define a total ordering." Clearly, (\_ _ -> if random() then GT else LT) does not make for a total ordering. While you might say that this is no problem, since you don't actually want to *sort*, you might even get nontermination, if the implementation of sortBy happened to use the property "total ordering" for ensuring termination (as, e.g., a naive implementation of bubblesort does). Regards, Janis. -- Janis Voigtlaender http://wwwtcs.inf.tu-dresden.de/~voigt/ mailto:voigt@tcs.inf.tu-dresden.de
Markus.Schnell@infineon.com wrote:
Is there a library routine for random permutations?
I didn't find any and did a quick hack...
There are many algorithms. One, quite natural and quite fast (n log n; slower than linear, though...) consists in: 1. Generate N random numbers r_k, say, uniform between 0 and 1. 2. Form N pairs (1,r_1), (2,r_2), (3, r_3) ... (N,r_n). 3. Sort this list/vector wrt the *secon* (random) number. 4. In the sorted list the firs values (indices) give you the result. This is of course quite general, not restricted to any Haskell peculiarities. Jerzy Karczmarczuk
Markus.Schnell@infineon.com wrote:
Is there a library routine for random permutations?
Check out this link: http://www.nist.gov/dads/HTML/perfectShuffle.html -- Matthew Donadio (m.p.donadio@ieee.org)
participants (6)
-
Andreas Abel -
Janis Voigtlaender -
Jerzy Karczmarczuk -
Markus.Schnell@infineon.com -
Matthew Donadio -
Ralf Hinze