[comp.lang.functional] shuffling a list

quale@saavik.cs.wisc.edu (Douglas E. Quale) (09/05/90)

What is the best way to shuffle a list in a functional language?
It is easy to shuffle an array fairly, and in Scheme I would use
list->vector, shuffle the vector, and use vector->list.

--
Doug Quale

ken@aiai.ed.ac.uk (Ken Johnson) (09/06/90)

In article <5056@daffy.cs.wisc.edu> quale@saavik.cs.wisc.edu (Douglas E. 
Quale) writes:

>What is the best way to shuffle a list in a functional language?

Here is how I would do it in Prolog

1  Construct a random number generator.

2  Convert the list from the form [a,b,c,..] to a set of pairs of the
   form [N1-a,N2-b,N3-c] where N1, N2, N3... are random numbers

3  Keysort the list

4  Strip the keys off again.


-- Ken
-- 
Ken Johnson, AI Applications Institute, 80 South Bridge, Edinburgh EH1 1HN
E-mail ken@aiai.ed.ac.uk, phone 031-225 4464 extension 213
`I have read your article, Mr Johnson, and I am no wiser now than when I
started'.  -- `Possibly not, sir, but far better informed.'

lambert@spectrum.cs.unsw.oz.au (Tim Lambert) (09/06/90)

>>>>> On 5 Sep 90 16:58:26 GMT, quale@saavik.cs.wisc.edu (Douglas E. Quale) said:

> What is the best way to shuffle a list in a functional language?

In Miranda:

Riffle shuffle (as in a pack of cards) a list

> riffle:: [*] -> [*]
> riffle xs = riffle_merge (take half xs) (drop half xs)
>             where half = #xs div 2

Riffle merge two lists.  Take elements alternately from each
list to form the new list.

> riffle_merge (x:xs) (y:ys) = x:y:riffle_merge xs ys
> riffle_merge [] ys =  ys
> riffle_merge (x:xs) [] = x:xs

Now you can evaluate "iterate riffle [1..52]" and discover that eight
riffle shuffles put a deck back the way it started.

:-)

Tim

np@doc.ic.ac.uk (Nigel Perry) (09/07/90)

Douglas originally said something like "in Scheme I'd list->vector,
shuffle, vector->list", why not do this in a FL? Hope+C code:

	dec shuffle : list(alpha) -> list(alpha);
	--- shuffle l <= vectortolist(shuffle_vector listtovector(l))

where shuffle_vector is your favorite vector shuffling algorithm
(Hope+C provides map_vector & perm_vector H.O. functions to help
you with these.)

Now all we need is the Haskell solution....


Nigel


---
Nigel Perry                                  Department of Computing
                                             Imperial College
Janet: np@uk.ac.ic.doc                       London
ARPA:  np%uk.ac.ic.doc@ucl-cs                SW7
Uucp:  np@icdoc.UUCP, ukc!icdoc!np           England

zmacx07@doc.ic.ac.uk (Simon E Spero) (09/08/90)

In article <5056@daffy.cs.wisc.edu> quale@saavik.cs.wisc.edu (Douglas E. Quale) writes:
>What is the best way to shuffle a list in a functional language?
>It is easy to shuffle an array fairly, and in Scheme I would use
>list->vector, shuffle the vector, and use vector->list.
>
>--
>Doug Quale
      ----- no relation to Dan?

This is a functional version written (mostly) in  lazy Hope+C, for use 
in implementations where sharing analysis can be performed. I haven't tried it
,mainly cause I don't know of any such implementation that's put all the bits
together- as a separate question, how many production FLs have sharing analysis
wired in? 
   Curious of Kensington

-------- cut to pieces here -------

dec shuffle : list(alpha) -> list(alpha);
dec shuffle : vector(alpha) -> vector(alpha);

--- shuffle l <= vectortolist (shuffle (listtovector l));

--- shuffle v <=
	let vsize == vectorlength(v)
	let newvec == map_vector(id,v) in
        let swap_em == 
		lambda 
		([],vec) => v
		(a::b,vec) =>
		 let (x,y) == (rand(vsize),rand(vsize)) in
		 let t == vref(vec,x) in
		 let t2 == vref(vec,y) in
    !force 'em!	 let dummy == t = t and t2 = t2 in
		 if dummy  then 
		           let vec2 == vset(vec,x,t2) in
		 	   swap_em(b,vset(vec2,y,t))
	  	  else      error([(999,"Reality fault: core dumped")])
	   	 in
	 swap_em(first(vsize/2,[]),newvec)


Notes: rand(x) is a magic function which returns a num of range 1..n
       Of course, in reality there's a lazy list that gets passed 
       around - close your eyes and use you're imagination :-)

       	vset(v,i,x) returns a copy of v with v[i] set to x. If no sharing 
       	analysis is done, then this code will end up creating vsize copies
       	of the array- not fun. However the way that this code is written, 
       	using two temp variables to avoid  problems when  x = y , and
       	the use of the copy newvec to localise all sharing problems to
       	this function, should be sufficient to bludgeon even the simplest
       	sharing analyser into action. 
        The main problem is in ensuring that the two calls to vref are 
	evaluated before the calls to vset. I can't think of any way to force `
	this without also forcing the values returned by those calls. As 
	a result, this algorithm is stricter than it ought to be. Is there
	any way around this?

       In each pass through swap_em, the orignal vector is not referenced 
       after a vset has been applied to it (at this stage an evaluation
       order can be defined. vref should be strict
       vset isn't provided in standard Hope+C - you can emulate it with
       perm_vector (like map_vector, only the function supplied is given
       an element's index as well as its value. 

	first is the function discussed ad nauseum in this forum - first(n,[])
	produces a list of  n bottoms (and why not).
--
zmacx07@uk.ac.ic.doc | sispero@cix.co.uk |    ..!mcsun!ukc!slxsys!cix!sispero
------------------------------------------------------------------------------
The Poll Tax.    | Saddam Hussein runs Lotus 123 on | DoC,IC,London SW7 2BZ
I'm Not. Are you?| Apple Macs.|   I love the smell of Sarin in the morning

jgarland@kean.ucs.mun.ca (09/10/90)

In article <5056@daffy.cs.wisc.edu>, quale@saavik.cs.wisc.edu (Douglas E. Quale) writes:
> What is the best way to shuffle a list in a functional language?
> It is easy to shuffle an array fairly, and in Scheme I would use
> list->vector, shuffle the vector, and use vector->list.
> 
> --
> Doug Quale

If all you need is *local* sorting, as in shuffling cards for a game 
where it is impt to break up *runs* but not to randomly resort the whole 
deck, you can use a modified form of insert_sort.   A very few shuffles 
thoroughly breaks up runs.  As for complete randomization, it takes 
this algorithm 200+ shuffles of a list composed of the integers from 1 to 52
before the sum of the 1st half of the list approximates that of the 2nd half 
(say 2-3 seconds on a 12.5 Mz AT with the following PDC programme) 
--a necessary precondition for complete randomization.  I did not test 
for other forms of nonrandomization.

**************Listing for randomizing insert sort*********************

constants

listsize     =  52   % Arbitrary length.
shuffletimes = 500   % Arbitrary.  Set to 500 for investigative purposes.

domains

list = integer*

predicates

makelist(list,list,integer)
shuffle(list,list,integer)
r_insert_sort(list,list)
r_insert(integer,list,list)
r_order

clauses
 /*
 1.  makelist/3: Clauses to generate a list of integers from 1 to listsize.
 2.  shufflelist/3: Clauses to take a list and run it through 
     r_insert_sort n=shuffletimes times.
*/

/* Randomizing insert_sort predicates: */

    r_insert_sort([],[]).
    r_insert_sort([X|Tail],Sorted_list) :-
       r_insert_sort(Tail,Sorted_tail),
       r_insert(X,Sorted_tail,Sorted_list).
    
       r_insert(X,[Y|Sorted_list],[Y|Sorted_list1]) :-
          r_order,   % a_ord(X,Y) or d_ord(X,Y) here in insert_sort
          !,
          r_insert(X,Sorted_list,Sorted_list1).
      
       r_insert(X,Sorted_list,[X|Sorted_list]).
  
       r_order :- random(2,Ran),Ran = 0.  % Substitute random ordering, not  
                   % a_ord(X,Y) :- X>Y. or d_ord equivalent as for insert_sort
   
goal
   makelist([],List,listsize),
   shuffle(List,Shuffledlist,shuffletimes).
***************End of listing******************************

If this suits your purposes, it's easy and fast.

John Garland

jgarland@mun                       Bitnet
jgarland@kean.ucs.mun.ca           Internet