解决这个问题的最佳方法似乎是构建一个iterator可以生成排列列表,而不是使用类似的函数permn
它预先生成整个列表(一个昂贵的操作)。
寻找构建此类对象的指导的一个很好的地方是迭代工具 http://docs.python.org/library/itertools.htmlPython 标准库中的模块。 Itertools 已针对 R 部分重新实现为同名的包 http://crantastic.org/packages/itertools.
下面是一个使用 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 http://en.wikiquote.org/wiki/Donald_Knuth:“小心上面代码中的错误;我只是尝试过,没有证明它是正确的。”
对于序列的前 3 个排列1:10
, 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
尽管与 Python 算法相比,由于大量使用循环,成本要高得多permn
。 Python 实际上用 C 语言实现了这个算法,弥补了解释循环的低效率。
代码可用GitHub 上的要点 https://gist.github.com/832723。如果有人有更好的想法,请离开!