Parece que la mejor manera de abordar esto sería construir un iterador que podría producir la lista de permutaciones en lugar de utilizar una función como permn
que genera toda la lista en la delantera (una operación costosa).
Un excelente lugar para buscar orientación sobre la construcción de dichos objetos es el módulo itertools en la biblioteca estándar de Python. Itertools se ha vuelto a implementar parcialmente para R como a package of the same name.
El siguiente es un ejemplo que utiliza itertools de R para poner en práctica un puerto del generador de Python que crea iteradores de permutaciones:
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)))
}
Para citar incorrectamente Knuth: "Cuidado con los errores en el código anterior; Solo lo he probado, no he probado que sea correcto."
Para los 3 primeros permutaciones de la secuencia 1:10
, permn
paga un precio muy alto para el cálculo de permutaciones innecesarios:
> 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)
Sin embargo, el iterador devuelto por permutations
puede ser consultada por sólo los tres primeros elementos que ahorran una gran cantidad de cálculos:
> 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
El algoritmo de Python genera permuta ciones en un orden diferente que permn
.
Informática todas las permutaciones es todavía posible:
> system.time(all_perms <- as.list(permutations(1:10)))
user system elapsed
498.601 0.672 499.284
Aunque mucho más caro que el algoritmo de Python hace un uso intensivo de los bucles en comparación con permn
. Python realmente implementa este algoritmo en C que compensa la ineficiencia de los bucles interpretados.
El código está disponible in a gist on GitHub. Si alguien tiene una idea mejor, ¡bájate!
¿Me falta algo? ¿Por qué no permutaciones (n) [1: N,]? – Stompchicken
permutaciones (100,5) [1:10,] llevará algunas veces ... Me imagino que ese es el problema. – Spacedman
Probablemente deberías tener algún tipo de definición de lo que quieres decir con "primero". – John