Estou tentando mostrar ao meu filho como a codificação pode ser usada para resolver um problema apresentado por um jogo, além de ver como o R lida com o big data. O jogo em questão é chamado "Lucky 26". Neste jogo, os números (1 a 12 sem duplicatas) são posicionados em 12 pontos em uma estrela de David (6 vértices, 6 cruzamentos) e as 6 linhas de 4 números devem adicionar 26. Das aproximadamente 479 milhões de possibilidades (12P12 ) aparentemente existem 144 soluções. Tentei codificar isso no R da seguinte maneira, mas a memória é um problema que parece. Eu agradeceria muito qualquer conselho para avançar a resposta se os membros tiverem tempo. Agradecendo antecipadamente aos membros.
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
DesertProject
fonte
fonte
x<- 1:elements
e mais importanteL1 <- y[,1] + y[,3] + y[,6] + y[,8]
. Isso não seria realmente ajudar o seu problema de memória para que você pode sempre olhar para rcpprm(list=ls())
seu MRE. Se alguém copiar e colar em uma sessão ativa, poderá perder seus próprios dados.Respostas:
Aqui está outra abordagem. É baseado em um post do blog do MathWorks de Cleve Moler , autor do primeiro MATLAB.
Na postagem do blog, para economizar memória, o autor permite apenas 10 elementos, mantendo o primeiro elemento como o elemento principal e o sétimo como o elemento base. Portanto, apenas
10! == 3628800
permutações precisam ser testadas.No código abaixo,
1
para10
. Há um total10! == 3628800
deles.11
como o elemento principal e mantenha-o fixo. Realmente não importa onde as atribuições começam, os outros elementos estarão nas posições relativas corretas .for
loop.Isso deve produzir a maioria das soluções, dar ou receber rotações e reflexões. Mas isso não garante que as soluções sejam únicas. Também é razoavelmente rápido.
fonte
Na verdade, existem 960 soluções. A seguir, fazer uso de
Rcpp
,RcppAlgos
* , eoparallel
pacote para obter a solução em apenas6 seconds
usando 4 núcleos. Mesmo se você optar por usar uma abordagem de encadeamento único com os R's básicoslapply
, a solução será retornada em cerca de 25 segundos.Primeiro, escrevemos um algoritmo simples
C++
que verifica uma permutação específica. Você notará que usamos uma matriz para armazenar todas as seis linhas. Isso é para desempenho, pois utilizamos a memória cache de maneira mais eficaz do que usar 6 matrizes individuais. Você também deve ter em mente queC++
usa a indexação baseada em zero.Agora, usando os argumentos
lower
e , podemos gerar pedaços de permutações e testá-los individualmente para manter a memória sob controle. Abaixo, escolhi testar cerca de 4,7 milhões de permutações por vez. A saída fornece os índices lexicográficos das permutações de 12! tal que a condição Lucky 26 seja satisfeita.upper
permuteGeneral
Agora, verificamos o uso
permuteSample
e o argumentosampleVec
que permite gerar permutações específicas (por exemplo, se você passar 1, ele fornecerá a primeira permutação (por exemplo1:12
)).Por fim, verificamos nossa solução com a base R
rowSums
:* Eu sou o autor de
RcppAlgos
fonte
Para permutações, o rcppalgos é ótimo. Infelizmente, existem 479 milhões de possibilidades com 12 campos, o que significa que consome muita memória para a maioria das pessoas:
Existem algumas alternativas.
Colha uma amostra das permutações. Ou seja, faça apenas 1 milhão em vez de 479 milhões. Para fazer isso, você pode usar
permuteSample(12, 12, n = 1e6)
. Veja a resposta de @ JosephWood para uma abordagem um pouco semelhante, exceto que ele obtém 479 milhões de permutações;)Crie um loop no rcpp para avaliar a permutação na criação. Isso economiza memória porque você acabaria criando a função para retornar apenas os resultados corretos.
Aborde o problema com um algoritmo diferente. Vou me concentrar nessa opção.
Novo algoritmo com restrições
Os segmentos devem ter 26
Sabemos que cada segmento de linha na estrela acima precisa somar 26. Podemos adicionar essa restrição à geração de nossas permutações - nos fornecer apenas combinações que somam 26:
Grupos ABCD e EFGH
Na estrela acima, eu pintei três grupos de maneira diferente: ABCD , EFGH e IJLK . Os dois primeiros grupos também não têm pontos em comum e também estão em segmentos de interesse on-line. Portanto, podemos adicionar outra restrição: para combinações que somam 26, precisamos garantir que ABCD e EFGH não tenham sobreposição de números. Os 4 números restantes serão atribuídos ao IJLK .
Permissão através dos grupos
Precisamos encontrar todas as permutações de cada grupo. Ou seja, só temos combinações que somam 26. Por exemplo, precisamos pegar
1, 2, 11, 12
e criar1, 2, 12, 11; 1, 12, 2, 11; ...
.Cálculos finais
O último passo é fazer as contas. Eu uso
lapply()
eReduce()
aqui para fazer uma programação mais funcional - caso contrário, muito código seria digitado seis vezes. Veja a solução original para uma explicação mais completa do código matemático.Troca de ABCD e EFGH
No final do código acima, aproveitei a possibilidade de trocar
ABCD
eEFGH
obter as permutações restantes. Aqui está o código para confirmar que sim, podemos trocar os dois grupos e estar corretos:atuação
No final, avaliamos apenas 1,3 milhão das 479 permutações e apenas embaralhamos apenas 550 MB de RAM. Demora cerca de 0,7s para executar
fonte
Aqui está a solução para o amiguinho:
fonte