# [R] permutation cycles

Martin Maechler maechler at stat.math.ethz.ch
Tue Aug 26 17:55:10 CEST 2008

```I'm replying in public to this,
since it relates to an R-help thread of April 15-16, 2008
(and am also adding back 'References:' to one of those postings
of mine).

>>>>> Mark Segal < mark at biostat ucsf >
>>>>>     on Mon, 25 Aug 2008 11:25:05 -0700 writes:

> Hi Martin,
> You posted the following code for obtaining cycles of a permutation:

## MM: Peter's function is so compact and elegant,
## now try to optimize for speed() :
cycLengths2 <- function(p) {
n <- length(p)
x <- integer(n)
ii <- seq_len(n)
for (i in ii) {
z <- ii[!x][1] # index of first unmarked x[] entry
if (is.na(z)) break
repeat { ## mark x[] <- i for those in i-th cycle
x[z] <- i
z <- p[z]
if (x[z]) break
}
}
## Now, table(x) gives the cycle lengths,
## where split(seq_len(n), x) would give the cycles list
tabulate(x, i - 1L) ## is quite a bit faster than the equivalent
## table(x)
}

> But, while it is the case that
> split(seq_len(n), x)
> gives the members of the respective cycle lists, their cycle ordering
> is destroyed: it is replaced with increasing order.
> Do you have an efficient way of obtaining (preserved) cycle order?

Yes, indeed, the above code  (a version of an original function
by Peter Dalgaard) does not very easily lend itself to get the
cycles "in order".
Well, as a matter of fact, I've spent too much time achieving
that but the resulting solution was slower than
the function that  Barry Rowlingson had sent me back in April
which exactly solves the problem of finding the
"cycle decomposition" of permutation vector p[]:

cycles <- function(p) {

## From: Barry Rowlingson < at Lancaster ac UK>
## To: Martin Maechler < at ETH Zurich>
## Subject: Re: [R] sign(<permutation>) in R ?
## Date: Tue, 15 Apr 2008 18:15:19 +0100

## with minor tweaks by MM
cycles <- list()
ii <- seq_along(p)
while(!all(is.na(p))) {
start <- ii[!is.na(p)][1]# == which(!is.na(p))[1]
cycle <- start
i <- start
repeat {
nextV <- p[i]
p[i] <- NA
if(nextV == start) # cycle is closed
break
## else
cycle <- c(cycle,nextV)
i <- nextV
}
cycles <- c(cycles,list(cycle))
}
return(cycles)
}

You can check that it is working
(and also find that it's reasonably fast),
e.g., with

> p <- sample(12); rbind(seq_along(p), p)

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
1    2    3    4    5    6    7    8    9    10    11    12
p   11    4    5    3    6    2    9   12    1     8    10     7

> str(cycles(p))
List of 2
\$ : int [1:7] 1 11 10 8 12 7 9
\$ : int [1:5] 2 4 3 5 6

---
Martin Maechler, ETH Zurich

```