Permute all unique enumerations of a vector in R

前端 未结 7 794
忘了有多久
忘了有多久 2020-11-29 06:21

I\'m trying to find a function that will permute all the unique permutations of a vector, while not counting juxtapositions within subsets of the same element type.

7条回答
  •  心在旅途
    2020-11-29 06:31

    The following function (which implements the classic formula for repeated permutations just like you did manually in your question) seems quite fast to me:

    upermn <- function(x) {
        n <- length(x)
        duplicates <- as.numeric(table(x))
        factorial(n) / prod(factorial(duplicates))
    }
    

    It does compute n! but not like permn function which generates all permutations first.

    See it in action:

    > dat <- c(1,0,3,4,1,0,0,3,0,4)
    > upermn(dat)
    [1] 18900
    > system.time(uperm(dat))
       user  system elapsed 
      0.000   0.000   0.001 
    

    UPDATE: I have just realized that the question was about generating all unique permutations not just specifying the number of them - sorry for that!

    You could improve the unique(perm(...)) part with specifying unique permutations for one less element and later adding the uniqe elements in front of them. Well, my explanation may fail, so let the source speak:

    uperm <- function(x) {
    u <- unique(x)                    # unique values of the vector
    result <- x                       # let's start the result matrix with the vector
    for (i in 1:length(u)) {
        v <- x[-which(x==u[i])[1]]    # leave the first occurance of duplicated values
        result <- rbind(result, cbind(u[i], do.call(rbind, unique(permn(v)))))
    }
    return(result)
    }
    

    This way you could gain some speed. I was lazy to run the code on the vector you provided (took so much time), here is a small comparison on a smaller vector:

    > dat <- c(1,0,3,4,1,0,0)
    > system.time(unique(permn(dat)))
       user  system elapsed 
      0.264   0.000   0.268 
    > system.time(uperm(dat))
       user  system elapsed 
      0.147   0.000   0.150 
    

    I think you could gain a lot more by rewriting this function to be recursive!


    UPDATE (again): I have tried to make up a recursive function with my limited knowledge:

    uperm <- function(x) {
        u <- sort(unique(x))
        l <- length(u)
        if (l == length(x)) {
            return(do.call(rbind,permn(x)))
        }
        if (l == 1) return(x)
        result <- matrix(NA, upermn(x), length(x))
        index <- 1
        for (i in 1:l) {
            v <- x[-which(x==u[i])[1]]
            newindex <- upermn(v)
            if (table(x)[i] == 1) {
                result[index:(index+newindex-1),] <- cbind(u[i], do.call(rbind, unique(permn(v))))
                } else {
                    result[index:(index+newindex-1),] <- cbind(u[i], uperm(v))
                }
            index <- index+newindex
        }
        return(result)
    }
    

    Which has a great gain:

    > system.time(unique(permn(c(1,0,3,4,1,0,0,3,0))))
       user  system elapsed 
     22.808   0.103  23.241 
    
    > system.time(uperm(c(1,0,3,4,1,0,0,3,0)))
       user  system elapsed 
      4.613   0.003   4.645 
    

    Please report back if this would work for you!

提交回复
热议问题