Christian Marks

Ecumenical dispatches from the London Library

Order of a permutation in clojure

leave a comment »

The non-functional style of the code of the previous post goes against the spirit of Clojure. The code below is a translation into Clojure of the code to compute the order of a (random) permutation from previous posts. By comparison with Common Lisp, construction of random permutations is considerably simplified in Clojure, which like Python provides a random shuffle function.

The function order-perm below is an adaptation of the constant space algorithm of C. J. Gower, from page 4 of Knuth, D. E., “Selected Papers on Analysis of Algorithms,” CSLI Lecture Notes Number 102, CSLI Publications, (2000). The order of a permutation is the least common multiple of its cycle lengths; the function order-perm makes use of the associativity of the least common multiple. The function (next-cycle-leader perm i) locates the first index of the cycle of the permutation perm at or below index i, as i ranges over the permutation indices in order. If the function (next-cycle-leader perm i) returns i itself, then i is the least index of the cycle containing index i (in index order, by induction). That accounts for the if form in the recur function of order-perm: if the index i is the least index of the cycle containing it, the binding of order in the loop form is updated with the least common multiple of the length of the cycle at index i and the current value of order, which is the least common multiple of the cycles visited so far, in order of cycle leader index; otherwise the binding of order is unchanged.

(ns knuth
    (:require [clojure.contrib.math :as math]))

(defn random-perm [n]  (shuffle (range n)))

(defn next-cycle-leader [perm i]
	(loop [j (nth perm i)]
		(if (<= j i)
			j
			(recur (nth perm j)))))

(defn cycle-length [perm i]
	(loop [cycle 1 j (nth perm i)]
		(if (= i j)
			cycle
			(recur (inc cycle) (nth perm j)))))

(defn order-perm [perm]
	(let [n (count perm)]
		(loop [i 0 order 1]
			(if (= i n)
				order
		   	    (recur (inc i)	
       				   (if (= i (next-cycle-leader perm i))
						     (math/lcm (cycle-length perm i) order)
                              order))))))

A test in the read-eval-print loop:


knuth=> (order-perm [9 1 6 0 3 2 5 4 8 7])
15
knuth=> (order-perm [6 3 4 7 0 5 2 1 8 9])
12
knuth=> (order-perm [8 0 1 5 2 9 7 4 6 3])
21

The running time compares favorably with common lisp, though I ran out of heap space in the Java Virtual Machine:

user=> (load "knuth")                        
nil
user=> (ns knuth)                            
nil
knuth=> (time (order-perm (random-perm 100000)))
"Elapsed time: 360.895 msecs"
1065606748036679276883000
knuth=> (time (order-perm (random-perm 1000000)))
"Elapsed time: 5584.238 msecs"
753574706077650707341441647806967057600
knuth=> (time (order-perm (random-perm 10000000)))
java.lang.OutOfMemoryError: Java heap space (NO_SOURCE_FILE:0)

Advertisement

Written by Christian Marks

May 19, 2011 at 6:44 PM

Posted in Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.