Permutations

+ Associated files

close

NB: An exercise relating to the material covered in this tutorial can be found on the Exercises page.

slippery chicken comes with a number of predefined algorithmic functions that are designed to help the user generate new lists from existing ones by making variations of the original lists or their elements. These functions all fall under the general category of permutations, and can be found in the permutations.lsp source file. This page will provide a brief introduction to the most frequently used of these functions.

General attributes of the permutation functions

All of the permutation functions in slippery chicken use a random algorithm that defaults to a fixed-seed state. This means that random operations return the same results each time they are started (providing they are run on the same Lisp). Most of the functions have optional arguments that allow the user to reset the random seed, as well as to disable it.

The individual permutation functions

+ permutations

The permutations function systematically generates a list of all possible permutations of a set of consecutive integers beginning with zero. It takes as its only argument an integer that indicates the number of consecutive numbers (including 0) to use. In other words, calling the function with 4 will produce permutations of the numbers 0, 1, 2, and 3.

The order of the resulting list of permutations will be the same each time the function is called.

(permutations 4)

=>
((0 1 2 3) (1 0 2 3) (0 2 1 3) (2 0 1 3) (1 2 0 3) (2 1 0 3) (0 1 3 2)
 (1 0 3 2) (0 3 1 2) (3 0 1 2) (1 3 0 2) (3 1 0 2) (0 2 3 1) (2 0 3 1)
 (0 3 2 1) (3 0 2 1) (2 3 0 1) (3 2 0 1) (1 2 3 0) (2 1 3 0) (1 3 2 0)
 (3 1 2 0) (2 3 1 0) (3 2 1 0))

close

+ inefficient-permutations

The inefficient-permutations function returns a randomly shuffled, non-systematic list of all possible permutations of a set of consecutive integers starting with zero. It's only required argument is an integer, which, like the permutations function, indicates the number of consecutive numbers (starting from 0) to use.

This function also has the three optional keyword arguments :max, :skip, and :fix.

The :max argument takes an integer that indicates the maximum number of permutations to return.

The :skip argument takes an integer that indicates the number of permutations of those returned that should be skipped. This number always applies to consecutive permutations from the beginning of the list.

The :fix argument takes T or NIL to indicate whether the random shuffling of the list of permutations returned should be performed with the same (fixed) random seed each time or not. T indicates that a fixed seed should be used.

(inefficient-permutations 4 :max 7 :skip 2 :fix t)

=> ((2 0 3 1) (1 0 2 3) (1 2 3 0) (0 2 3 1) (2 1 0 3))

close

+ permutate

The permutate function produces a list of all possible permutations of an original list of specified elements of any type.

NB: These lists get very long very fast! A list of 5 elements, for example, returns a new list of 120 elements; an original list of 6 elements produces a new list of 720. Original lists that have more than 8 elements cause the function to write the results to a file rather than to the print buffer.

(permutate '(a b c d))

=> ((A B C D) (B A C D) (A C B D) (C A B D) (B C A D) (C B A D) (A B D C)
    (B A D C) (A D B C) (D A B C) (B D A C) (D B A C) (A C D B) (C A D B)
    (A D C B) (D A C B) (C D A B) (D C A B) (B C D A) (C B D A) (B D C A)
    (D B C A) (C D B A) (D C B A))

close

+ inefficiently-permutate

The inefficiently-permutate function returns a randomly shuffled, non-systematic list of all possible permutations of an original list of elements of any type. The new list is returned as a flat list by default.

NB: These lists get very long very fast! Original lists of 8 or more elements result in the function's output being written to a file.

In addition to the one required argument of the list to be permutated, this function has the four optional keyword arguments :max, :skip, :fix, and :sublists.

The :max argument takes an integer that indicates the maximum number of permutations to return.

The :skip argument takes an integer that indicates the number of permutations of those returned that should be skipped. This number always applies to consecutive permutations from the beginning of the list.

The :fix argument takes T or NIL to indicate whether the random shuffling of the list of permutations returned should be performed with the same (fixed) random seed each time or not. T indicates that a fixed seed should be used.

The :sublists argument takes .T or NIL to indicate whether the permutations are to be returned as a list of sublists (T) or as a flat list.

(inefficiently-permutate '(a b c d) :max 7 :skip 2 :fix t)

=> (C A D B B A C D B C D A A C D B C B A D)

(inefficiently-permutate '(a b c d) :max 7 :skip 2 :fix t :sublists t)

=> ((C A D B) (B A C D) (B C D A) (A C D B) (C B A D))

close

+ shuffle

The shuffle function creates a random ordering of a given list or segment of a given list. In addition to its required argument of the list to shuffle, it also has the five optional keyword arguments :start, :end, :copy, :fix and :reset.

The :start argument takes a 0-based integer to indicate the index of the first element of the list to be shuffled.

The :end argument takes a 0-based integer to indicate the index of the last element of the list to be shuffled.

The :copy argument takes a T or NIL to indicate whether the process is to be applied to a copy of the list (T) or to the original instance of the list (destructive).

The :fix argument takes a T or NIL to indicate whether the operation is to be performed with the same (fixed) random seed (T) or not.

The :reset argument takes a T or NIL to indicate whether the Lisp's random state is to be reset (T) or not before applying the operation.

(shuffle '(1 2 3 4 5 6 7) :start 1 :end 5 :fix t :reset t)

=> (1 5 4 3 2 6 7)

close

+ multi-shuffle

The multi-shuffle function repeatedly applies the shuffle function a specified number of times to a specified list. In addition to its two required arguments of the list to shuffle and the number of times to shuffle it, it also has the five optional keyword arguments :start, :end, :copy, :fix and :reset.

The :start argument takes a 0-based integer to indicate the index of the first element of the list to be shuffled.

The :end argument takes a 0-based integer to indicate the index of the last element of the list to be shuffled.

The :copy argument takes a T or NIL to indicate whether the process is to be applied to a copy of the list (T) or to the original instance of the list (destructive).

The :fix argument takes a T or NIL to indicate whether the operation is to be performed with the same (fixed) random seed (T) or not.

The :reset argument takes a T or NIL to indicate whether the Lisp's random state is to be reset (T) or not before applying the operation.

(multi-shuffle '(1 2 3 4 5 6 7) 11 :start 1 :end 5 :fix t :reset t)

=> (1 5 3 4 2 6 7)

close

+ multi-shuffle-with-perms

The multi-shuffle-with-perms function returns one permutation of a shuffled version of the specified list. It takes as its two arguments the list to shuffle and an integer that indicates the number of consecutive shuffles to be collected in the list from which the resulting permutation is selected. This function always uses a fixed random seed.

(multi-shuffle-with-perms '(0 1 2 3 4) 7)

=> (3 1 4 2 0)

close

+ move-repeats

The move-repeats function does not create permutations per se, but can be used to remove any consecutive repetitions of elements in existing lists, such as those created by the other permutation functions listed above.

This function moves, when possible, one of any two elements of a list that are repeated consecutively to the first point in the list where no consecutive repetition is created. It only moves elements to the right. If no non-repeating place can be found in the remainder of the list, the element is moved to the end of the list.

This function can be applied to both simple lists and lists of sublists. If it is used with sublists, the last element of each sublist is checked for repetition with the first element of the next.

(move-repeats '(1 2 3 3 4 5 6 7 8 8 9 10))

=> (1 2 3 4 3 5 6 7 8 9 8 10)

(move-repeats '((a b c) (c a b) (c a b) (d e f) (a b c) (g h i)))

=> ((A B C) (D E F) (C A B) (C A B) (A B C) (G H I))

close

+ random-rep

Like the move-repeats function, the random-rep function does not create permutations per se. It is mentioned here, however, since its function is related to the manner by which many of the permutation functions operate.

Like Lisp's own random function, this function returns a pseudo-random, non-negative number that is less than the value specified as its first (required) argument. The random-rep function then has an additional, optional argument (T or NIL) that allows for the random state to be reset, allowing for the same list of pseudo-random numbers to be produced at each call.

(loop repeat 10 collect (random-rep 5))

=> (2 4 0 4 4 2 1 3 2 4)

(loop repeat 10 collect (random-rep 5 t))

=> (3 3 3 3 3 3 3 3 3 3)

close

+ Using the permutation functions in a piece

Any of the permutation functions can be used, of course, to generate self-referential lists of any kind of element, and can therefore be implemented to algorithmically generate any kind of slippery chicken data.

The following example code uses the inefficiently-permutate function to algorithmically generate the rhythm sequences of the rthm-seq-palette for the piece

(let* ((perms (inefficiently-permutate '(e (e) s (e.) q s s (e)) 
                                       :max 91 
                                       :sublists t))
       (mini
        (make-slippery-chicken
         '+mini+
         :ensemble '(((fl (flute :midi-channel 1))
                      (ob (oboe :midi-channel 2))
                      (bn (bassoon :midi-channel 3))))
         :tempo-map '((1 (q 84)))
         :set-palette '((0 ((fs2 b2 d4 a4 d5 e5 a5 d6)))
                        (1 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6)))
                        (2 ((cs3 fs3 e4 a4 e5 a5 e6)))
                        (3 ((fs2 cs3 e4 a4 b4 e5 a5 b5 e5)))
                        (4 ((d2 a2 e4 fs4 gs4 b4 e5 b5)))
                        (5 ((a2 e3 e4 fs4 gs4 b4 cs5 e5 b5)))
                        (6 ((cs3 fs3 fs4 gs4 a4 cs5 a5 cs6)))
                        (7 ((fs2 cs3 fs4 gs4 a4 b4 cs5 fs5)))
                        (8 ((e2 a2 cs4 fs4 gs4 a4 b4 e5 gs5 b5 e6)))
                        (9 ((d2 a2 fs4 gs4 a4 e5 a5 e6)))
                        (10 ((a2 d2 e4 fs4 a4 e5 a5))))
         :set-limits-high '((ob (0 a5 100 a5))
                            (bn (0 g3 100 g3)))
         :set-limits-low '((fl (0 d5 100 d5)))
         :set-map `((1 ,(loop for sn from 0 below (length perms)
                           collect (mod sn 11)))) ; mod 11 = 11 sets 
         :rthm-seq-palette (loop for p in perms
                              for rs-id from 0
                              collect `(,rs-id
                                        ((((4 4) ,@(nth rs-id perms)))
                                         :pitch-seq-palette ((1 3 2 5 6)))))
         :rthm-seq-map `((1 ((fl
                              ,(loop for rs from 0 below (length perms)
                                  collect rs))
                             (ob
                              ,(loop for rs from 0 below (length perms)
                                  collect 
                                    (mod (1+ rs) (length perms))))
                             (bn
                              ,(loop for rs from 0 below (length perms)
                                  collect 
                                    (mod (+ rs 2) (length perms))))))))))
  (auto-beam mini t nil)
  (map-over-bars mini 1 nil nil #'consolidate-rests-max)
  (auto-slur mini '(fl ob bn))
  (midi-play mini)
  (cmn-display mini)
  (write-lp-data-for-all mini))

permutations-piece.png
First 15 measures of the resulting piece as typeset by LilyPond

close