Para las permutaciones, rcppalgos es genial. Desafortunadamente, hay 479 millones de posibilidades con 12 campos, lo que significa que ocupa demasiada memoria para la mayoría de las personas:
library(RcppAlgos)
elements <- 12
permuteGeneral(elements, elements)
#> Error: cannot allocate vector of size 21.4 Gb
Hay algunas alternativas
Tome una muestra de las permutaciones. Es decir, solo hacer 1 millón en lugar de 479 millones. Para hacer esto, puedes usar permuteSample(12, 12, n = 1e6)
. Vea la respuesta de @ JosephWood para un enfoque algo similar, excepto que muestra 479 millones de permutaciones;)
Cree un bucle en rcpp para evaluar la permutación en la creación. Esto ahorra memoria porque terminaría creando la función para devolver solo los resultados correctos.
Aborde el problema con un algoritmo diferente. Me enfocaré en esta opción.
Nuevo algoritmo con restricciones
Los segmentos deben ser 26
Sabemos que cada segmento de línea en la estrella de arriba necesita sumar hasta 26. Podemos agregar esa restricción para generar nuestras permutaciones; denos solo combinaciones que sumen 26:
# only certain combinations will add to 26
lucky_combo <- comboGeneral(12, 4, comparisonFun = '==', constraintFun = 'sum', limitConstraints = 26L)
Grupos ABCD y EFGH
En la estrella de arriba, he coloreado tres grupos de manera diferente: ABCD , EFGH e IJLK . Los dos primeros grupos tampoco tienen puntos en común y también están en línea segmentos de interés. Por lo tanto, podemos agregar otra restricción: para las combinaciones que suman 26, debemos asegurarnos de que ABCD y EFGH no tengan superposición de números. A IJLK se le asignarán los 4 números restantes.
library(RcppAlgos)
lucky_combo <- comboGeneral(12, 4, comparisonFun = '==', constraintFun = 'sum', limitConstraints = 26L)
two_combo <- comboGeneral(nrow(lucky_combo), 2)
unique_combos <- !apply(cbind(lucky_combo[two_combo[, 1], ], lucky_combo[two_combo[, 2], ]), 1, anyDuplicated)
grp1 <- lucky_combo[two_combo[unique_combos, 1],]
grp2 <- lucky_combo[two_combo[unique_combos, 2],]
grp3 <- t(apply(cbind(grp1, grp2), 1, function(x) setdiff(1:12, x)))
Permutar a través de los grupos.
Necesitamos encontrar todas las permutaciones de cada grupo. Es decir, solo tenemos combinaciones que suman 26. Por ejemplo, necesitamos tomar 1, 2, 11, 12
y hacer 1, 2, 12, 11; 1, 12, 2, 11; ...
.
#create group perms (i.e., we need all permutations of grp1, grp2, and grp3)
n <- 4
grp_perms <- permuteGeneral(n, n)
n_perm <- nrow(grp_perms)
# We create all of the permutations of grp1. Then we have to repeat grp1 permutations
# for all grp2 permutations and then we need to repeat one more time for grp3 permutations.
stars <- cbind(do.call(rbind, lapply(asplit(grp1, 1), function(x) matrix(x[grp_perms], ncol = n)))[rep(seq_len(sum(unique_combos) * n_perm), each = n_perm^2), ],
do.call(rbind, lapply(asplit(grp2, 1), function(x) matrix(x[grp_perms], ncol = n)[rep(1:n_perm, n_perm), ]))[rep(seq_len(sum(unique_combos) * n_perm^2), each = n_perm), ],
do.call(rbind, lapply(asplit(grp3, 1), function(x) matrix(x[grp_perms], ncol = n)[rep(1:n_perm, n_perm^2), ])))
colnames(stars) <- LETTERS[1:12]
Cálculos finales
El último paso es hacer los cálculos. Utilizo lapply()
y Reduce()
aquí para hacer una programación más funcional; de lo contrario, se escribiría mucho código seis veces. Consulte la solución original para obtener una explicación más detallada del código matemático.
# creating a list will simplify our math as we can use Reduce()
col_ind <- list(c('A', 'B', 'C', 'D'), #these two will always be 26
c('E', 'F', 'G', 'H'), #these two will always be 26
c('I', 'C', 'J', 'H'),
c('D', 'J', 'G', 'K'),
c('K', 'F', 'L', 'A'),
c('E', 'L', 'B', 'I'))
# Determine which permutations result in a lucky star
L <- lapply(col_ind, function(cols) rowSums(stars[, cols]) == 26)
soln <- Reduce(`&`, L)
# A couple of ways to analyze the result
rbind(stars[which(soln),], stars[which(soln), c(1,8, 9, 10, 11, 6, 7, 2, 3, 4, 5, 12)])
table(Reduce('+', L)) * 2
2 3 4 6
2090304 493824 69120 960
Intercambiando ABCD y EFGH
Al final del código anterior, aproveché que podemos intercambiar ABCD
y EFGH
obtener las permutaciones restantes. Aquí está el código para confirmar que sí, podemos intercambiar los dos grupos y estar en lo correcto:
# swap grp1 and grp2
stars2 <- stars[, c('E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'I', 'J', 'K', 'L')]
# do the calculations again
L2 <- lapply(col_ind, function(cols) rowSums(stars2[, cols]) == 26)
soln2 <- Reduce(`&`, L2)
identical(soln, soln2)
#[1] TRUE
#show that col_ind[1:2] always equal 26:
sapply(L, all)
[1] TRUE TRUE FALSE FALSE FALSE FALSE
Actuación
Al final, evaluamos solo 1.3 millones de las 479 permutaciones y solo barajamos 550 MB de RAM. Tarda alrededor de 0.7s en correr
# A tibble: 1 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
<bch:expr> <bch> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl>
1 new_algo 688ms 688ms 1.45 550MB 7.27 1 5
x<- 1:elements
y lo más importanteL1 <- y[,1] + y[,3] + y[,6] + y[,8]
. Esto realmente no ayudaría a su problema de memoria, por lo que siempre puede buscar en rcpp