Manhattan Ameobas em crescimento

11

Um gráfico *** ameoba **** é um tipo de árvore cujos nós todos têm valores de 0 a algum número inteiro não negativo N e qualquer nó específico com valor x <N se conecta a x + 1 nós distintos com valores x + 1

Gráfico de Ameoba para N = 3: (Denotado A 3 )

ameoba 3

Observe que os 2 não têm permissão para compartilhar nenhum dos 3; exatamente três 3 devem "pertencer" a cada 2.

Desafio

Sua tarefa é "aumentar" indutivamente esses gráficos de ameoba em uma grade bidimensional, minimizando avidamente a distância de Manhattan entre os nós:

  • Caso base: Um 0 é simplesmente o gráfico 0.
  • Etapa indutiva: Um N + 1 é gerado colocando iterativamente os novos nós com valor de N + 1 o mais próximo possível dos nós de valores de N na estrutura A N existente . (Só pode ser o mais próximo possível, pois os pontos mais próximos já podem estar preenchidos.)

Para a etapa indutiva, o procedimento geral que você deve seguir é:

for each existing node P with value N:
    for each new N+1 valued node Q you need to connect to P: //this loops N+1 times
        find the set of vacant spots that are minimally distant from P //by Manhattan distance
        place Q in any of these vacant spots

(Um procedimento diferente com saída indistinguível é bom.)

Exemplo de crescimento para A 4 :

A0 is always the same:

0

For A1 I happen to put the 1 to the right of the 0 (it has to go on one of the 4 open sides):

01

For A2 I happen to put the two 2's above and to the right of the 1:

 2
012


For A3 I find that one of the six 3's I must place cannot be directly next to a 2, so I put in one of the next closest places:

 3
323
0123
  33 <-- this 3 is distance two away from its 2

The process continues in A4. Note that I'm iterating over each 3 and placing four 4's next to it or as close as possible, then moving to the next 3 (the order of 3's does not matter):

 444
443444
4323444
4012344
 44334
  4444
   44

Always keep in mind that nodes cannot be "shared".

Programa

O programa que você escreve deve receber um número de 0 a 8 (inclusive) e gerar um gráfico válido de ameoba, usando o padrão de crescimento indutivo explicado acima.

O que acontece além de 8 não importa.

(A 8 contém 46234 nós que o estão pressionando. Qualquer coisa além de A 8 seria muito longe. Agradecemos a Martin Büttner por perceber isso.)

A entrada deve vir de stdin ou a linha de comando e a saída deve ir para stdout ou um arquivo.

Exemplos (tirados diretamente de cima)

Input: 0
Output:

0

Input: 1
Output:

01

Input: 2
Output:

 2
012

Input: 3
Output:

 3
323
0123
  33

Input: 4
Output:

 444
443444
4323444
4012344
 44334
  4444
   44

* Esse tipo de gráfico já pode ter um nome. Eu admito que acabei de inventá-los. ;)


fonte
À luz da taxa de crescimento fatorial, a pergunta poderia ser alterada de parar na A35 para parar em um arquivo de 1 Megabyte ou algo semelhante? A10 é a primeira ameba com mais de um milhão de caracteres.
Isaacg
@ MartinBüttner Fiz o limite 8, que é de cerca de 50 mil nós. Ainda muito, mas esperançosamente gerenciável.

Respostas:

6

Mathematica, 353 288 285 275 bytes

n=Input[];f@_=" ";g={z={0,0}};i=f@z=0;For[r=Range,i++<n,g=Reap[(j=i;o={};For[d=0,j>0,o=Rest@o,If[o=={},o=Join@@({e={#,d-#},-e,e={d-#,-#},-e}&/@r@++d)];If[f[c=#&@@o+#]==" ",f@c=i;Sow@c;--j]])&/@g][[2,1]]];Riffle[(x=#;ToString@f@{x,#}&/@m~r~M)&/@r[m=Min@{g,0},M=Max@g],"
"]<>""

Ungolfed:

n = Input[];
f@_ = " ";
g = {z = {0, 0}};
i = f@z = 0;
For[r = Range, i++ < n,
  g = Reap[(
        j = i;
        o = {}; 
        For[d = 0, j > 0, o = Rest@o,
         If[o == {}, 

          o = Join @@ ({e = {#, d - #}, -e, e = {d - #, -#}, -e} & /@  
              r@++d)
          ];  
         If[f[c = # & @@ o + #] == " ",
          f@c = i;
          Sow@c;
          --j 
          ]   
         ]   
        ) & /@ g
     ][[2, 1]] 
  ];  
Riffle[(
     x = #;
     ToString@f@{x, #} & /@ m~r~M
     ) & /@ r[m = Min@{g, 0}, 
    M = Max@g
    ], "
  "] <> ""

Aqui está um exemplo de saída para n = 5:

      5
     5555     
    555555    
   5555555    
  555555555   
 55555555555  
5555554445555 
5555544444555 
 5555443305555
 55554432144555
 55555443234555
  5555544344555
   555554445555
    5555555555
      5555555 
       55555  
       55     

A entrada 8leva cerca de 4,5 minutos.

Para uma rápida descrição do meu algoritmo:

Estou usando duas tabelas de consulta fe g. O primeiro é apenas um mapa esparso contendo as células não vazias. A última é uma lista que contém todos os pares de coordenadas para cada valor de célula (acho que nem preciso acompanhar os antigos aqui). Estou percorrendo as coordenadas gpara estender todas as células da última iteração. Para fazer isso, percorro as distâncias de Manhattan, criando todos os vetores possíveis para cada distância e verificando se a célula resultante ainda está vazia (nesse caso, eu a preencho). Repita até que novas células tenham sido criadas.

Quando termino, encontro as coordenadas mínima e máxima ge crio uma grade apropriada, que é preenchida pesquisando as células f. O resto é apenas juntar tudo em uma única sequência com quebras de linha.

Martin Ender
fonte
5

C - 309305301 275 bytes

Meh, muito tempo ... se ao menos alguém pudesse digitar #Dou algo assim #define, então C seria realmente ótimo. É claro que os -Dsinalizadores do compilador são possíveis, mas isso me parece uma trapaça, ter caracteres diferentes dos que estão no arquivo de origem.

Instruções de execução:

Seja cuidadoso! A primeira tecla pressionada após o início do programa constitui a entrada. Após uma entrada de caractere que não seja de 0 a 8, quem sabe que coisas indefinidas acontecerão.

#define F(D,O)x=*r+O d;for(c=d;h*c--;x+=D)!g[x]?g[*w++=x]=l,--h:5;
L=400;g[1<<18];n;s;*r;*w;*m;h;l;d;x;main(c){n=getch()-32;r=w=g+L*L;for(l=g[*w++=80200]=16;l++<n;)for(m=w;r<m;r++)for(d=1,h=l-16;h;d++){F(L+1,-)F(L-1,-L*)F(-L+1,L*)F(~L,)}for(n=L*L;--n;)putch(n%L?g[n]+32:10);}

Versão sem golfe (mas já pensando em golfe futuro):

void exit(int);

#define L 400

#define FIND(D, X0)   x = *pread X0 d; \
                for(c = d; c--; x+=D) { \
                    if(x%L == 0 || x%L == L-1 || x/L == 0 || x/L == L-1) \
                        exit(5); \
                    if(!g[x]) { \
                        g[*pwrite++ = x] = '0' + l; \
                        if(!--children) \
                            goto pnext; \
                    } \
                }

main()
{
    int n = getch() - '0';
    //char g[3] = {};
    char g[L*L] = {};
    int plist[46324];

    int *pwrite = plist, *pread = plist;
    *pwrite++ = L/2*L + L/2;
    g[*plist] = '0';
    int factorial = 1;
    int l,  c, parents, children, d, x;
    for(l = 1; l <= n; l++) {
        for(parents = factorial; parents--; pread++) {
            children = l;
            for(d = 1; ; d++) {
                FIND(L + 1, - )
                FIND(L - 1, -L* )
                FIND(-L + 1, +L* )
                FIND(-L - 1, + )
            }
            pnext:;
        }
        factorial *= l;
    }
    int i;
    for(i = L*L; i--; )
        putch(i%L ? (g[i] ? g[i] : ' ') : '\n');
}

Edit: eu percebi que, desde que movi as declarações para fora de main (), as matrizes não podem mais ser alocadas na pilha, por isso estou livre para usar a memória de maneira profusa, sem risco de transbordamento.

feersum
fonte
2

Ruby - 296

g=[s=' ']*d=10**6
$*[g[50500]=0].to_i.times{|c|d.times{|x|g[x]==c&&(r=1;a=c;(4.times{|v|r.times{|w|g[q=x+r*(1e3*(v-1-v/2)+v%2-v/2)+w*(1e3*~0**(v/2)+~0**v)]==s&&a>~0?(g[q]=c+1;a-=1):0}};r+=1)while~0<a)}}
g=g.join.scan(/.{1000}/)
g.map{|s|s[/\d/]&&(puts s[g.map{|s|s[/\A */].size}.min..-1].rstrip)}

Ligeiramente não-destruído.

g=[s=' ']*d=10**6 # Initialize a big 1d array as a 2d grid
$*[g[50500]=0].to_i.times{|c| # For n times
    d.times{|x| # For each index in the grid
        g[x]==c&&( # If the element at x is equal to the current growth stage, c
            r=1;   # Initial manhattan radius = 1
            a=c;   # a is number of times the ameoba must replicate
            (4.times{|v| # For each of the 4 sides of the manhattan diamond
                r.times{|w| # For each node in each side
                    # Spawn the 'c+1' ameoba's from the c ameobas... 
                    # The messy formula gives the index of the space in the grid to try spawning
                    g[q=x+r*(1e3*(v-1-v/2)+v%2-v/2)+w*(1e3*~0**(v/2)+~0**v)]==s&&a>~0?(g[q]=c+1;a-=1):0 
                }
            };
            r+=1 # Increase the raidus of the manhattan diamond by one
            ) while~0<a # while not enough ameoba's have been spawned
        )
    }
}
g=g.join.scan(/.{1000}/) # Join the 1d array into a huge string and slice it into rows
# Strip away the empty spaces all around the graph and print it
g.map{|s|s[/\d/]&&(puts s[g.map{|s|s[/\A */].size}.min..-1].rstrip)} 
Vetorizado
fonte
2

APL (Dyalog) (121)

{0::0⋄V←,⍳⍴Z←' '⍴⍨2/M←⌈4×.5*⍨3÷⍨+/!⍳⍵⋄Z[G;G←⌈M÷2]←'0'⋄Z⊣{⍵∘{⍵∘{+((⊃F[⍋+/¨|(F←V/⍨,Z=' ')-⊂⍺])⌷Z)←⍕⍵}¨⍺/⍺}¨V/⍨,Z=⍕⍵-1}¨⍳⍵}⎕

Características de desempenho: é O (n!). No meu sistema, até n = 5 é instantâneo; n = 6 leva um segundo, n = 7 leva um minuto en = 8 leva uma hora.

Versão sem golfe

Teste:

      {0::0⋄V←,⍳⍴Z←' '⍴⍨2/M←⌈4×.5*⍨3÷⍨+/!⍳⍵⋄Z[G;G←⌈M÷2]←'0'⋄Z⊣{⍵∘{⍵∘{+((⊃F[⍋+/¨|(F←V/⍨,Z=' ')-⊂⍺])⌷Z)←⍕⍵}¨⍺/⍺}¨V/⍨,Z=⍕⍵-1}¨⍳⍵}⎕
⎕:
      5





           5555             
          555555            
         55555555           
        5555445555          
       555544445555         
      55554433445555        
     5555444323445555       
    5555544321455555        
     555554430455555        
     555555444555555        
       555555555555         
        5555555555          
         55555555           
          55555             
           555              

Explicação:

  • {... }⎕: leia uma linha do teclado, avalie-a e passe o resultado para a função.
  • 0::0: se o outro código gerar um erro, retorne um único 0. Isso ocorre porque a matemática falha ao tentar calcular o tamanho de um gráfico com 0 nós, o que acontece quando a saída deve ser 0. (A versão anterior tinha ⍵=0:0, (se a entrada for 0retornar, 0faça o gráfico), mas 0::0(apenas tente e retorne 0se falhar) é mais curta.)
  • M←⌈4×.5*⍨3÷⍨+/!⍳⍵: supondo que a saída seja um círculo aproximado (isso funciona), some os fatoriais de 1até (= área da saída), divida por 3 (perto o suficiente para pi), pegue a raiz quadrada (dando o raio de saída), multiplique por 4, e pegue o teto. Isso fornece o dobro do diâmetro do círculo, para que a saída se encaixe com espaço de sobra. Guarde isso em M.
  • V←,⍳⍴Z←' '⍴⍨2/M: crie uma matriz de espaços M-por-M e armazene-a Z. Isso manterá a saída. Armazene uma lista das coordenadas de todos os elementos em V.
  • Z[G;G←⌈M÷2]←'0': defina o elemento do meio de Zcomo 0.
  • Z⊢{... }¨⍳⍵: retorne Z, após aplicar a seguinte função aos números 1em :
    • ⍵∘{... }V/,Z=⍕⍵-1: para cada elemento Zcom o valor do nó anterior:
      • ⍵∘{... }⍺/⍺: para o nó atual, N vezes,
        • ⊃F[⍋+/¨|(F←V/⍨,Z=' ')-⊂⍺]: obtenha o espaço livre mais próximo do nó atual,
        • (... ⌷Z)←⍕⍵: e configure esse espaço Zpara o valor do nó atual.
marinus
fonte