R:所有排列中的前N个

时间:2011-02-17 16:22:11

标签: r

我正在寻找一个

的功能
  • 可以列出所有n!给定输入向量的排列(通常只是序列1:n
  • 也可以只列出所有n的前N个!排列

满足第一个要求,例如permn()来自包combinatpermutations()来自包e1071,或permutations()来自包gtools }。但是,我很肯定,某些​​软件包还有另一个功能,它也提供了第二个功能。我用了一次,但后来忘记了它的名字。

编辑: “前N”的定义是任意的:函数只需要一个始终遵循的内部枚举方案,并且应该在计算N个排列后中断。

正如Spacedman正确指出的那样,至关重要的是该函数不会计算比实际需要更多的排列(以节省时间)。

编辑 - 解决方案:我记得我正在使用的是numperm()来自sna个包。 numperm(4, 7)给出元素1:4的第7个排列,对于前N个,必须循环。

2 个答案:

答案 0 :(得分:6)

似乎最好的方法是构建一个迭代器,它可以生成排列列表,而不是使用像permn之类的函数,它可以预先生成整个列表(昂贵的操作)。

寻找构建此类对象的指导的一个好地方是Python标准库中的itertools模块。 Itertools已经部分重新实现为R a package of the same name

以下是使用R的itertools实现Python生成器的端口的示例,该端口为排列创建迭代器:

require(itertools)

permutations <- function(iterable) {
  # Returns permutations of iterable. Based on code given in the documentation
  # of the `permutation` function in the Python itertools module:
  #   http://docs.python.org/library/itertools.html#itertools.permutations
  n <- length(iterable)
  indicies <- seq(n)
  cycles <- rev(indicies)
  stop_iteration <- FALSE

  nextEl <- function(){
    if (stop_iteration){ stop('StopIteration', call. = FALSE) }
    if (cycles[1] == 1){ stop_iteration <<- TRUE } # Triggered on last iteration

    for (i in rev(seq(n))) {
      cycles[i] <<- cycles[i] - 1
      if ( cycles[i] == 0 ){
        if (i < n){
          indicies[i:n] <<- c(indicies[(i+1):n], indicies[i])
        }
        cycles[i] <<- n - i + 1
      }else{
        j <- cycles[i]
        indicies[c(i, n-j+1)] <<- c(indicies[n-j+1], indicies[i])
        return( iterable[indicies] )
      }
    }
  }

  # chain is used to return a copy of the original sequence 
  # before returning permutations. 
  return( chain(list(iterable), new_iterator(nextElem = nextEl)) )

}
  

错误引用Knuth:“请注意上述代码中的错误;我只是尝试过它,而不是证明它是正确的。”

对于序列1:10的前3个排列,permn为计算不必要的排列付出了沉重的代价:

> system.time( first_three <- permn(1:10)[1:3] )
   user  system elapsed 
134.809   0.439 135.251 
> first_three
[[1]]
 [1]  1  2  3  4  5  6  7  8  9 10

[[2]]
 [1]  1  2  3  4  5  6  7  8 10  9

[[3]]
 [1]  1  2  3  4  5  6  7 10  8  9)

但是,permutations返回的迭代器只能查询前三个元素,这些元素可以节省大量的计算量:

> system.time( first_three <- as.list(ilimit(permutations(1:10), 3)) )
   user  system elapsed 
  0.002   0.000   0.002 
> first_three
[[1]]
 [1]  1  2  3  4  5  6  7  8  9 10

[[2]]
 [1]  1  2  3  4  5  6  7  8 10  9

[[3]]
 [1]  1  2  3  4  5  6  7  9  8 10

Python算法确实以与permn不同的顺序生成排列。

计算所有排列仍然是可能的:

> system.time( all_perms <- as.list(permutations(1:10)) )
   user  system elapsed 
498.601   0.672 499.284

虽然与permn相比,Python算法大量使用循环要贵得多。 Python实际上在C中实现了这个算法,它补偿了解释循环的低效率。

代码可用in a gist on GitHub。如果有人有更好的主意,请离开!

答案 1 :(得分:3)

在我的R / combinat版本中,函数permn()的长度超过了30行。一种方法是复制permn并将其更改为提前停止。