Shuffle
October 20, 2009
It is easy to shuffle an array by stepping through the array, swapping each element with a forward element (an element at an index greater than or equal to the current element) until the next-to-last element is reached. It is harder to shuffle a linked list, because lists don’t permit ready access to any element other than the first.
Your task is to write functions that shuffle an array and a linked list. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
Here’s a PLT version, which works like the Perl version:
(define (shuffle x) (sort x #:key (lambda _ (random)) #:cache-keys? #t))
Here’s an example of the array shuffle in perl (without using the pre-existing library). Perl doesn’t generally support linked lists as all arrays have the push/pop/etc functionality included.
Perl array shuffle example
Here is an implementation in Clojure. Its native datatypes are immutable, so for the array shuffle I’m using Java’s ArrayList. The collection shuffler works on any ordered Clojure collection (immutable lists as well as vectors); because values are immutable, we are modifying what the variables point to, which has to be done within a software transaction with dosync.
To shuffle a linked list without converting it into an array or treating it as if it were an array, create two temporary lists, left and right, and then for each element in the original list, move that element randomly into either left or right. Then use recursion shuffle each of the left and right lists, combine them, and return the combined list.
Of course, doing two recursive calls can result in lots of memory usage, especially since the random selection could potentially put all elements of the original into left, or all into right… which adds to the stack depth, while accomplishing little work.
Instead, after partitioning into left and right, join the lists back together before shuffling, so that one of the two recursive calls can be changed into a loop.
Here’s some lisp code to do it:
Alternatively, here’s some C code:
This would be used as:
PS: I’ve only tested the lisp code, since I’m still working on installing gcc on my computer.
But since the algorithm is the same, they should work the same.
I haven’t looked closely, but isn’t your algorithm O(n log n), like any other divide-and-conquer algorithm? The advantage of the list->vector->knuth-shuffle->list algorithm is that it works in O(n) time.
MY c IMPLEMENTATION