Estoy tratando de mostrarle a mi hijo cómo se puede usar la codificación para resolver un problema planteado por un juego, así como ver cómo R maneja los grandes datos. El juego en cuestión se llama "Lucky 26". En este juego, los números (1-12 sin duplicados) se colocan en 12 puntos en una estrella de David (6 vértices, 6 intersecciones) y las 6 líneas de 4 números deben sumar 26. De las aproximadamente 479 millones de posibilidades (12P12 ) aparentemente hay 144 soluciones. Traté de codificar esto en R de la siguiente manera, pero la memoria es un problema que parece. Agradecería mucho cualquier consejo para avanzar la respuesta si los miembros tienen tiempo. Agradeciendo a los miembros de antemano.
library(gtools)
x=c()
elements <- 12
for (i in 1:elements)
{
x[i]<-i
}
soln=c()
y<-permutations(n=elements,r=elements,v=x)
j<-nrow(y)
for (i in 1:j)
{
L1 <- y[i,1] + y[i,3] + y[i,6] + y[i,8]
L2 <- y[i,1] + y[i,4] + y[i,7] + y[i,11]
L3 <- y[i,8] + y[i,9] + y[i,10] + y[i,11]
L4 <- y[i,2] + y[i,3] + y[i,4] + y[i,5]
L5 <- y[i,2] + y[i,6] + y[i,9] + y[i,12]
L6 <- y[i,5] + y[i,7] + y[i,10] + y[i,12]
soln[i] <- (L1 == 26)&(L2 == 26)&(L3 == 26)&(L4 == 26)&(L5 == 26)&(L6 == 26)
}
z<-which(soln)
z
r
bigdata
permutation
Proyecto Desierto
fuente
fuente
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 rcpprm(list=ls())
en su ERM. Si alguien copia y pega en una sesión activa, podría perder sus propios datos.Respuestas:
Aquí hay otro enfoque. Se basa en una publicación de blog de MathWorks de Cleve Moler , autor del primer MATLAB.
En la publicación del blog, para ahorrar memoria, el autor permuta solo 10 elementos, manteniendo el primer elemento como el elemento principal y el séptimo como el elemento base. Por lo tanto, solo las
10! == 3628800
permutaciones necesitan ser probadas.En el siguiente código,
1
a10
. Hay un total10! == 3628800
de ellos.11
como elemento vértice y manténgalo fijo. Realmente no importa dónde comiencen las tareas, los otros elementos estarán en las posiciones relativas correctas .for
bucle.Esto debería producir la mayoría de las soluciones, dar o tomar rotaciones y reflexiones. Pero no garantiza que las soluciones sean únicas. También es razonablemente rápido.
fuente
En realidad hay 960 soluciones. A continuación hacemos uso de
Rcpp
,RcppAlgos
* , y elparallel
paquete para obtener la solución en poco más de6 seconds
medio de 4 núcleos. Incluso si elige utilizar un enfoque de un solo subproceso con base Rlapply
, la solución se devuelve en unos 25 segundos.Primero, escribimos un algoritmo simple
C++
que verifica una permutación particular. Notarás que usamos una matriz para almacenar las seis líneas. Esto es para el rendimiento, ya que utilizamos la memoria caché de manera más efectiva que el uso de 6 matrices individuales. También tendrá que tener en cuenta queC++
usa indexación basada en cero.Ahora, usando los argumentos
lower
yupper
enpermuteGeneral
, podemos generar fragmentos de permutaciones y probarlos individualmente para mantener la memoria bajo control. A continuación, he elegido probar alrededor de 4.7 millones de permutaciones a la vez. ¡La salida da los índices lexicográficos de las permutaciones de 12! tal que se cumpla la condición Lucky 26.Ahora, verificamos el uso
permuteSample
y el argumentosampleVec
que le permite generar permutaciones específicas (por ejemplo, si pasa 1, le dará la primera permutación (es decir1:12
)).Finalmente, verificamos nuestra solución con la base R
rowSums
:* Soy el autor de
RcppAlgos
fuente
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:
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:
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.
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 hacer1, 2, 12, 11; 1, 12, 2, 11; ...
.Cálculos finales
El último paso es hacer los cálculos. Utilizo
lapply()
yReduce()
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.Intercambiando ABCD y EFGH
Al final del código anterior, aproveché que podemos intercambiar
ABCD
yEFGH
obtener las permutaciones restantes. Aquí está el código para confirmar que sí, podemos intercambiar los dos grupos y estar en lo correcto: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
fuente
Aquí está la solución para el pequeño:
fuente