<p>
<font face="ARIAL">I asked for the best way to shuffle a list on comp.lang.scheme a few years ago. The discussion went wild; you can see it on Google Groups if you want. After that, I wrote this summary:<br></font></p>
<p><font face="ARIAL">It is easy to shuffle a vector by stepping through the vector, swapping each
element with a forward element (including possibly the element itself) until
the next-to-last element is reached. The classic description is given by
Knuth in AoCP, Volume 2, Section 3.4.2, Algorithm P:
</font></p>
<p><font face="ARIAL"><font face="COURIER"><pre>(define (shuffle v)<br> (do ((n (length x) (- n 1))) ((zero? n) v))<br> (let* ((r (random n)) (t (vector-ref v r)))<br> (vector-set! v r (vector-ref v (- n 1)))<br>
(vector-set! v (- n 1) t))))<br></pre></font></font></p>
<p>
<font face="ARIAL"><font face="ARIAL">But shuffling a list is harder, because lists don't permit O(1) access to any
element except the first. Joe Marshall provides this method of shuffling a
list by partitioning it into two pieces deterministically, shuffling them
recursively, then merging them randomly:
</font></font></p>
<p><font face="ARIAL"><font face="ARIAL"><font face="COURIER"><pre>(define (shuffle xs)<br> (if (or (null? xs) (null? (cdr xs))) xs<br> (let split ((xs xs) (odd '()) (even '()))<br> (if (pair? xs)<br>
(split (cdr xs) (cons (car xs) even) odd)<br> (let merge ((odd (shuffle odd)) (even (shuffle even)))<br> (cond ((null? odd) even)<br> ((null? even) odd)<br> ((zero? (random 2)) (cons (car odd) (merge (cdr odd) even)))<br>
(else (cons (car even) (merge odd (cdr even))))))))))<br></pre></font></font></font></p>
<p>
<font face="ARIAL"><font face="ARIAL"><font face="ARIAL">Al Petrofsky proposes this somewhat faster code that first partitions the list
randomly, then randomly merges them:
</font></font></font></p>
<p><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="COURIER"><pre>(define (shuffle xs)<br> (let shuffle ((xs xs) (acc '()))<br> (if (null? xs) acc<br> (if (null? (cdr xs)) (cons (car xs) acc)<br>
(let split ((xs xs) (x1 '()) (x2 '()))<br> (if (null? xs)<br> (if (null? x1)<br> (split x2 '() '())<br> (shuffle x1 (shuffle x2 acc)))<br>
(if (zero? (random 2))<br> (split (cdr xs) (cons (car xs) x1) x2)<br> (split (cdr xs) x1 (cons (car xs) x2)))))))))<br></pre></font></font></font></font></p>
<p>
<font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL">If you want, you can always do Perl's omigod Schwartzian transform:
</font></font></font></font></p>
<p><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="COURIER"><pre>(define (shuffle xs)<br> (map cdr<br> (sort (lambda (x y) (< (car x) (car y)))<br> (map (lambda (x) (cons (random 1.0) x)) xs))))<br>
</pre></font></font></font></font></font></p>
<p>
<font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL">But the fastest method of shuffling a list is to convert it to a vector, use
Knuth's algorithm to shuffle the vector, then convert it back to a list; this
algorithm operates in linear time (all the others are n log n), and is very
fast despite the two type conversions:
</font></font></font></font></font></p>
<font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="ARIAL"><font face="COURIER"><pre>(define (shuffle x)<br> (do ((v (list->vector x)) (n (length x) (- n 1)))<br> ((zero? n) (vector->list v))<br>
(let* ((r (random n)) (t (vector-ref v r)))<br> (vector-set! v r (vector-ref v (- n 1)))<br> (vector-set! v (- n 1) t))))<br></pre></font></font></font></font></font></font><br><br><div class="gmail_quote">On Sun, Aug 9, 2009 at 12:39 PM, Jon Rafkind <span dir="ltr"><<a href="mailto:rafkind@cs.utah.edu">rafkind@cs.utah.edu</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div class="im">Amit Saha wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hello all,<br>
<br>
Here is my Scheme implementation of the "modern" version of the "Fisher Yates Shuffle" (<a href="http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle" target="_blank">http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle</a>). I am sharing this with the hope that it may be useful to someone in the community.<br>
<br>
<code><br>
#lang scheme<br>
<br>
;; Fisher-Yates shuffling algorithm in Scheme (plt-scheme)<br>
;; Amit Saha (<a href="http://amitksaha.wordpress.com" target="_blank">http://amitksaha.wordpress.com</a>; <a href="http://amitsaha.in" target="_blank">amitsaha.in</a>@<a href="http://gmail.com" target="_blank">gmail.com</a>)<br>
<br>
;; Useful to obtain a random shuffle of a list<br>
;; call with (shuffle <your list>)<br>
<br>
(define (shuffle deck)<br>
(let loop ((n (length deck)) (shuff_deck (list->vector deck)))<br>
(if (<= n 1)<br>
shuff_deck<br>
(begin<br>
(set! n (- n 1))<br>
(let* ([rand (random (+ 1 n))]<br>
[tmp (vector-ref shuff_deck rand)]<br>
)<br>
(vector-set! shuff_deck rand (vector-ref shuff_deck n))<br>
(vector-set! shuff_deck n tmp))<br>
(loop n shuff_deck)))))<br>
</code><br>
<br>
</blockquote></div>
Heres the one I wrote a while ago<br>
(define (randomize lst)<br>
(let ((v (list->vector lst)))<br>
(let loop ((max (sub1 (vector-length v))))<br>
(if (= 0 max)<br>
(vector->list v)<br>
(begin<br>
(let ((place (random max)))<br>
(let ((tmp (vector-ref v place)))<br>
(vector-set! v place (vector-ref v max))<br>
(vector-set! v max tmp)))<br>
(loop (sub1 max)))))))<br>
<br>
Notable differences are I don't set! the index variable, nor do I pass along the vector in the loop. Otherwise I guess they are about the same.<div><div></div><div class="h5"><br>
_________________________________________________<br>
For list-related administrative tasks:<br>
<a href="http://list.cs.brown.edu/mailman/listinfo/plt-scheme" target="_blank">http://list.cs.brown.edu/mailman/listinfo/plt-scheme</a><br>
</div></div></blockquote></div><br>