[R] sign(<permutation>) in R ?

Martin Maechler maechler at stat.math.ethz.ch
Wed Apr 16 18:51:12 CEST 2008


As alluded, I've got several off-line replies and suggestions,
notably Tobias Verbeke mentioning Matlab code {as an example of "pseudo
code" :-) } by Burkhardt on the web,
and Barry Rowlinson providing code similar to Peter Dalgaard's,
see below.
I've found myself python code on the web, unfortunately bugous
(only for *some* permutation vectors).

Of all I've got, Peter's code was clearly the best, when
n <- length(<permutation>) was not small.
For that reason, I've ``speeded'' Peter's code, and ended in
code that won hands down.

The simulation-testing of the diverse algorithms has been fun,
not the least finding that one "public" algorithm was faulty;
If I have a bit more time, I should write a small R news article
on this.
Here's the current best code
{and of course, a C version of that will be yet an order of
 magnitude faster} :

## 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)
}

## MM: Here's the transformation from the cycle lengths' vector to sign(<p>):
signCycLength <- function(clen) 1L - (sum(clen %% 2 == 0) %% 2)*2L

for the *sign* of a permutation 'p'
the above has to be called as  signCycLength(cycLengths2(p))
e.g.,

   p <- sample(50)
   signCycLength(cycLengths2(p))


Thank you, again for all the feedback, and suggestions,
Martin Maechler, ETH Zurich




>>>>>> Peter Dalgaard wrote yesterday

>> Martin Maechler wrote:
>> > Ok,
>> > thanks a lot, everyone!
>> >
>> > Yes, I should rather have started thinking a bit more myself,
>> > before going the easy route to R-help....
>> >
>> > Anyway, the most obvious algorithm,
>> > just putting things into place by swapping elements,
>> > and counting how many times you have to swap, is easy and
>> > quite efficient.
>> >
>> > I'll post R code later, being busy for the next few hours.
>> > Martin
>> >
>> >   
>> It is also quite easy to generate the cycles explicitly by a marking 
>> algorithm:
>> 
>> p <- sample(1:100)
>> x <- integer(100)
>> for (i in 1:100) {
>>     z <- which(!x)[1]
>>     if (is.na(z)) break
>>     repeat {
>>         x[z] <- i
>>         z <- p[z]
>>         if (x[z]) break
>>     }
>> }
>> table(x)
>> 
>> (the which(!x)[1] bit could be optimized somewhat if this had been C. 
>> Notice that the loop is essentially O(N) because every iteration marks 
>> one element of x)
>> 
>> 
>> >   
>> >>>>>> "MM" == Martin Maechler <maechler at stat.math.ethz.ch>
>> >>>>>>     on Tue, 15 Apr 2008 18:13:43 +0200 writes:
>> >>>>>>             
>> >
>> >     MM> I am looking for an algorithm (written in R (preferably) or C,
>> >     MM> but even pseudo-code in a text book maybe fine)
>> >     MM> to determine the sign of a permutation.
>> >
>> >     MM> What is that?  Well, a permutation is either even or odd, the
>> >     MM> sign is +1 or -1, respectively, see, e.g.,
>> >     MM> http://en.wikipedia.org/wiki/Signature_of_a_permutation
>> >     MM> which also says
>> >     >>> In practice, in order to determine whether a given permutation
>> >     >>> is even or odd, one writes the permutation as a product of
>> >     >>> disjoint cycles. The permutation is odd if and only if this
>> >     >>> factorization contains an odd number of even-length cycles.
>> >
>> >     MM> but I would not know how to algorithmically
>> >     MM> "write the permutation as a product of disjoint cycles"
>> >
>> >     MM> If you start looking at R code, 
>> >     MM> let's assume the permutation {\pi(i)}_{i=1..n} is simply given
>> >     MM> as the (integer) vector (\pi(1), \pi(2), ..., \pi(n))
>> >     MM> {or equivalently, a random permutation is simply found by  'sample(n)'}
>> >
>> >     MM> Thank you in advance for further pointers,
>> >     MM> or even working R code.
>> >
>> >     MM> Best regards,
>> >     MM> Martin Maechler, ETH Zurich
>> >

>> 
>> -- 
>>    O__  ---- Peter Dalgaard             Øster Farimagsgade 5, Entr.B
>>   c/ /'_ --- Dept. of Biostatistics     PO Box 2099, 1014 Cph. K
>>  (*) \(*) -- University of Copenhagen   Denmark      Ph:  (+45) 35327918
>> ~~~~~~~~~~ - (p.dalgaard at biostat.ku.dk)              FAX: (+45) 35327907



More information about the R-help mailing list