Estou trabalhando em um desafio envolvendo um autômato celular infinito 3D (cúbico) definido como:
Estado inicial : Começamos com 8 células posicionadas nos vértices de um cubo e cada célula está em um dos 4 estados {0: rosa, 1: vermelho, 2: verde, 3: azul}.
Crescimento : A cada passo, o cubo se expande inserindo novas células entre cada par de células existentes. O estado da nova célula é determinado pela soma dos estados de seus vizinhos mais próximos módulo 4. Note que, dependendo de sua posição, uma nova célula tem 2, 4 ou 8 vizinhos mais próximos (é a vizinhança de Moore 3D).
Para ilustrar isso, aqui estão algumas ilustrações dos primeiros passos:
Estado inicial
Passo 1
O nó central por exemplo será Verde porque tem 8 vizinhos, a soma dos tipos deles é 2 (Verde) + 1 (Vermelho) + 1 (Vermelho) + 0 (Rosa) + 2 (Verde) + 0 (Rosa) + 3 (Azul) + 1 (Vermelho) = 10% 4 = 2 (Verde)
Passo 2
Estamos interessados em rastrear a evolução do número de células de cada estado [0, 1, 2, 3]. Aqui estão as contagens para os primeiros passos:
- Inicial: 2, 3, 2, 1
- Passo 1: 6, 10, 7, 4
- Passo 2: 30, 36, 31, 28
- Etapa 3: 196, 192, 155, 186
- Etapa 4: 1'162, 1'276, 1'207, 1'268
Nosso objetivo é determinar a contagem de cada estado de célula na etapa 19. Dado o crescimento exponencial, a força bruta não é viável. Disseram-me que é possível resolver isso em menos de um segundo. Alguém tem alguma ideia de como resolver esse problema?
Agradecemos desde já a sua ajuda!
Podemos criar um contador para cada possível "pequeno" cubo, ou seja, um cubo cujos 8 cantos são vizinhos diretos. Então teríamos um contador para um cubo com a sequência de cantos ordenada de VERMELHO, VERMELHO, VERDE, ROSA, ROSA, VERDE, AZUL, AZUL, e contadores igualmente distintos para cada sequência distinta de oito cores (NB: a ordem importa).
Toda vez que "expandimos" para o próximo passo (geração), tal cubo 2x2x2 se torna um cubo 3x3x3: temos todas as informações para saber cada uma das 27 cores daquele cubo 3x3x3. Então subtraímos a contagem que tínhamos para o cubo 2x2x2 (pois ele não existe mais como tal), mas então aumentamos os contadores para os oito sub-cubos 2x2x2 que estão contidos no cubo 3x3x3. Novamente, sabemos as cores relevantes, então sabemos quais contadores aumentar.
Na 19ª geração (ou em cada geração, se desejarmos), podemos agregar esses contadores pela cor que o "primeiro" canto (ou seja, o mais próximo da origem) tem em cada cubo 2x2x2 possível. Dessa forma, teremos contado todos os "nós" na geração final, exceto os nós que estão em um dos 3 planos que estão no lado externo da estrutura completa e incluem o (último) nó que está mais distante da origem (o nó vermelho superior na entrada de exemplo). Para esses planos, podemos manter contadores para as faces 2D que estão nele e, no final, contar os contadores para os "primeiros" cantos dessas faces. Novamente, isso contará para quase todos os nós, exceto para os nós que estão nas três linhas 1D externas que incluem o (último) nó que está mais distante da origem. Também podemos manter contadores para essas arestas (que são pares de nós) e agregá-los com base na primeira cor de cada par. Isso nos deixa com apenas um nó que não é contabilizado, que é o último nó mais distante da origem.
Esta matriz de contadores tem este número de entradas:
Esse é um pedaço razoável de memória que, na maioria das linguagens de programação, pode ser iterado e atualizado em questão de alguns milissegundos.
Enquanto eu escrevia isso em JavaScript, descobri que o resultado final ultrapassa 2 53 , então tive que usar o tipo de dados BigInt para os contadores. No entanto, os números cabem em inteiros de 64 bits, então se você tem uma linguagem de programação com esse tipo de dados, não há necessidade de um tipo de dados Big Integer dinâmico.
Aqui está a implementação em JavaScript que você pode executar aqui:
Mesmo com a saída gerada, isso termina bem em um segundo no meu laptop. Escrito em uma linguagem de nível mais baixo (como C), deveria ser ainda mais rápido.