Vida de um verme


28

Termos e Condições

Um worm é qualquer lista de números inteiros não negativos e seu elemento mais à direita (ou seja, o último ) é chamado de cabeça . Se a cabeça não for 0, o worm tem um segmento ativo que consiste no bloco contíguo mais longo de elementos que inclui a cabeça e tem todos os seus elementos pelo menos tão grandes quanto a cabeça . O segmento ativo reduzido é o segmento ativo com a cabeça decrementada em 1. Por exemplo, o worm 3 1 2 3 2possui segmento ativo 2 3 2e o segmento ativo reduzido é 2 3 1.

Regras da evolução

Um worm evolui passo a passo da seguinte maneira:

Na etapa t (= 1, 2, 3, ...),
    se o cabeçalho for 0: exclua o cabeçalho
    mais: substitua o segmento ativo por t + 1 cópias concatenadas do segmento ativo reduzido.

Fato : Qualquer worm acaba evoluindo para a lista vazia , e o número de etapas para isso é a vida útil do worm .

(Detalhes podem ser encontrados em The Worm Principle , um artigo de LD Beklemishev. O uso de "list" para significar uma sequência finita e "head" para significar seu último elemento, são retirados deste artigo - ele não deve ser confundido com o uso comum de listas como um tipo de dados abstrato , onde head geralmente significa o primeiro elemento.)

Exemplos (segmento ativo entre parênteses)

Verme: 0,1

step    worm
         0(1)
1        0 0 0
2        0 0 
3        0
4           <- lifetime = 4

Verme: 1,0

step    worm
         1 0
1       (1)
2        0 0 0
3        0 0 
4        0
5           <- lifetime = 5

Verme: 1,1

step    worm
        (1 1)
1        1 0 1 0 
2        1 0(1) 
3        1 0 0 0 0 0
4        1 0 0 0 0
5        1 0 0 0
...
8       (1) 
9        0 0 0 0 0 0 0 0 0 0
10       0 0 0 0 0 0 0 0 0
...
18       0
19           <- lifetime = 19

Verme: 2

step    worm
        (2)
1       (1 1)
2        1 0 1 0 1 0
3        1 0 1 0(1)
4        1 0 1 0 0 0 0 0 0
5        1 0 1 0 0 0 0 0
6        1 0 1 0 0 0 0
...
10       1 0(1)
11       1 0 0 0 0 0 0 0 0 0 0 0 0 0
12       1 0 0 0 0 0 0 0 0 0 0 0 0
...
24      (1)
25       0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
50       0
51          <- lifetime = 51

Verme: 2,1

        (2 1)
1        2 0 2 0
2        2 0(2)
3        2 0(1 1 1 1)
4        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0
5        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0(1 1 1)
6        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
7        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0(1 1)
8        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0{1 0}^9
...
??          <- lifetime = ??      

Verme: 3

step    worm
        (3)
1       (2 2)
2       (2 1 2 1 2 1)
3        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 
4        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1(2)
5        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0(2 1 2 1 1 1 1 1 1 1)
6        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^7
7        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^6 (2 1 2 1 1 1 1 1 1) 
...      ...
??          <- lifetime = ??


a parte, de lado

A vida útil do worm é tipicamente enorme, como mostra os seguintes limites inferiores em termos da hierarquia padrão de funções de crescimento rápido f α :

worm                lower bound on lifetime
----------------    ------------------------------------------
11..10 (k 1s)       f_k(2)
2                   f_ω(2)
211..1 (k 1s)       f_(ω+k)(2)
2121..212 (k 2s)    f_(ωk)(2)
22..2 (k 2s)        f_(ω^k)(2)
3                   f_(ω^ω)(2)
...
n                   f_(ω^ω^..^ω)(2) (n-1 ωs)  >  f_(ε_0) (n-1)

Notavelmente, o worm [3] já tem uma vida útil que ultrapassa em muito o número de Graham , G:

f ω ω (2) = f ω 2 (2) = f ω2 (2) = f ω + 2 (2) = f ω + 1 (f ω + 1 (2)) >> f ω + 1 (64) > G.


Code Golf Challenge

Escreva o subprograma de função mais curto possível com o seguinte comportamento:

Entrada : Qualquer verme.
Saída : a vida útil do worm.

O tamanho do código é medido em bytes.


Aqui está um exemplo (Python, golfs to 167 bytes):

from itertools import *
def T(w):
    w=w[::-1]
    t=0
    while w:
        t+=1
        if w[0]:a=list(takewhile(lambda e:e>=w[0],w));a[0]-=1;w=a*(t+1)+w[len(a):]
        else:w=w[1:]
    return t


NB : Se t (n) é a vida útil do worm [n], a taxa de crescimento de t (n) é aproximadamente a da função Goodstein . Então, se isso pode ser golfed abaixo de 100 bytes, ele poderia muito bem dar uma resposta vencedora para o maior número Printable questão . (Para essa resposta, a taxa de crescimento pode ser muito acelerada sempre iniciando o contador de passos em n - o mesmo valor que o worm [n] - em vez de iniciá-lo em 0.)


Estou confuso com o seu código. Você disse que o cabeçalho é o elemento mais à direita , mas no seu exemplo de Python, você o trata como um w[0]* que é o elemento mais à esquerda dessa lista?

@LegoStormtroopr Se você pode considerar uma lista como tendo uma esquerda e uma direita. Se você considerar apenas o primeiro e o último, poderá mapear o mais para o primeiro ou o último da direita ao ler a sequência inicial - o que não faz parte da pergunta. Mas as entradas de função também não foram estritamente definidas.
— Bob

@LegoStormtroopr - Boa captura; Corrigi o código adicionando uma linha para reverter o worm de entrada, cuja cabeça deveria estar à direita (ou seja, o último elemento da lista w). É por eficiência que o programa opera no worm invertido.
— res

Obtendo o correto resposta por 2 1poderia ser pedir muito em um tempo razoável, mas um teste útil é que a seqüência deve começar (2 1), 2 0 2 0, 2 0 (2), 2 0 (1 1 1 1), ...
— Peter Taylor

1
@ThePlasmaRailgun - Parafraseando Harvey Friedman, os números derivados de funções no nível ε_0 na hierarquia de rápido crescimento (como a vida útil dos vermes) são completamente INOVÁVEIS em comparação com a TREE (3) .
— res

Respostas:


15

GolfScript ( 56 54 caracteres)

{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L;

Demonstração online

Eu acho que o principal truque aqui é provavelmente manter o worm em ordem inversa. Isso significa que é bastante compacto encontrar o comprimento do segmento ativo: .0+.({<}+??(onde o 0é adicionado como uma proteção para garantir que encontramos um elemento menor que a cabeça).


Como um aparte, algumas análises da vida útil do verme. Denotarei o worm como age, head tail(ou seja, na ordem inversa da anotação da pergunta) usando expoentes para indicar repetição na cabeça e na cauda: por exemplo, 2^3é 2 2 2.

Lema : para qualquer segmento ativo xs, existe uma função f_xsque se age, xs 0 tailtransforma em f_xs(age), tail.

Prova: nenhum segmento ativo pode conter a 0, portanto, a idade em que excluímos tudo antes da cauda é independente da cauda e, portanto, é apenas uma função de xs.

Lema : para qualquer segmento ativo xs, o verme age, xsmorre com a idade f_xs(age) - 1.

Prova: pelo lema anterior, se age, xs 0transforma em f_xs(age), []. A etapa final é a exclusão daquilo 0que não é tocado anteriormente porque nunca pode fazer parte de um segmento ativo.

Com esses dois lema, podemos estudar alguns segmentos ativos simples.

Para n > 0,

age, 1^n 0 xs -> age+1, (0 1^{n-1})^{age+1} 0 xs
              == age+1, 0 (1^{n-1} 0)^{age+1} xs
              -> age+2, (1^{n-1} 0)^{age+1} xs
              -> f_{1^{n-1}}^{age+1}(age+2), xs

então f_{1^n} = x -> f_{1^{n-1}}^{x+1}(x+2)(com base caso f_{[]} = x -> x+1, ou se você preferir f_{1} = x -> 2x+3). Vemos que f_{1^n}(x) ~ A(n+1, x)onde Aestá a função Ackermann – Péter.

age, 2 0 xs -> age+1, 1^{age+1} 0 xs
            -> f_{1^{age+1}}(age+1)

Isso é suficiente para entender 1 2( 2 1na notação da pergunta):

1, 1 2 -> 2, 0 2 0 2
       -> 3, 2 0 2
       -> f_{1^4}(4), 2
       -> f_{1^{f_{1^4}(4)+1}}(f_{1^4}(4)+1) - 1, []

Então, dada a entrada 2 1, esperamos uma saída ~ A(A(5,4), A(5,4)).

1, 3 -> 2, 2 2
     -> 3, 1 2 1 2 1 2
     -> 4, 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> 5, 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> f_{21212}^4(5) - 1

age, 2 1 2 1 2 -> age+1, (1 1 2 1 2)^{age+1}
               -> age+2, 0 1 2 1 2 (1 1 2 1 2)^age
               -> age+3, 1 2 1 2 (1 1 2 1 2)^age

e posso realmente começar a entender por que essa função cresce tão insanamente.


Muito legal. Penso que este programa também dará uma resposta vencedora ao programa de finalização mais curto, cujo tamanho de saída excede o número de Graham . (O vencedor atual possui 63 bytes de código Haskell.) Por exemplo, aos 55 bytes, algo como (como sou propenso a erros de sintaxe) 9{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L~calcula a vida útil do worm [9], que excede em muito o número de Graham - e pode ser golfe ainda mais.
— res

9

GolfScript, 69 62 caracteres

{0:?~%{(.{[(]{:^0=2$0+0=<}{\(@\+}/}{,:^}if;^?):?)*\+.}do;?}:C;

A função Cespera o worm na pilha e o substitui pelo resultado.

Exemplos:

> [1 1]
19

> [2]
51

> [1 1 0]
51

Fantástico! Certamente você pode modificar isso um pouco para também dar um vencedor definitivo para a pergunta "Maior número imprimível" .
— res

Eu não vi você postar nada por lá, então fui em frente e publiquei uma modificação desse código como o que acredito ser a resposta vencedora até agora - assumindo que o *e ^não esteja sendo usado como operador aritmético da multiplicação e exponencial. Certamente, se você quiser enviar sua própria resposta (sem dúvida superior) lá, eu removerei a minha com prazer.
— res

7

Ruby - 131 caracteres

Sei que isso não pode competir com as soluções GolfScript acima e tenho quase certeza de que isso pode reduzir uma pontuação ou mais caracteres, mas, sinceramente, estou feliz por ter conseguido resolver o problema sem esforço. Ótimo quebra-cabeça!

f=->w{t=0;w.reverse!;until w==[];t+=1;if w[0]<1;w.shift;else;h=w.take_while{|x|x>=w[0]};h[0]-=1;w.shift h.size;w=h*t+h+w;end;end;t}

Minha solução pré-golfe da qual deriva o acima:

def life_time(worm)
  step = 0
  worm.reverse!
  until worm.empty?
    step += 1
    if worm.first == 0
      worm.shift
    else
      head = worm.take_while{ |x| x >= worm.first }
      head[0] -= 1
      worm.shift(head.size)
      worm = head * (step + 1) + worm
    end
  end
  step
end

Dica genérica: muitos problemas de golfe funcionam com números inteiros não negativos; nesse caso, if foo==0pode ser aparado if foo<1. Isso pode economizar um caractere aqui.
— Peter Taylor

Aliás, acho fascinante que isso funcione sem um segundo reverse.
— Peter Taylor

Ah não. Apenas funciona nos casos de teste porque eles possuem apenas segmentos ativos palíndricos.
— Peter Taylor

Obrigado pela dica de golfe, @PeterTaylor. Além disso, boa captura no segundo reverso ausente. Adicionei. Tentarei reescrever isso de outra maneira sem usar o reverso mais tarde. Tenho certeza de que posso elsereduzir a cláusula a uma linha e depois trocar a if..else..enddeclaração por uma declaração ternária. Eu também poderia usar um lambda para salvar alguns caracteres, eu acho.
— OI

6

Sclipting (43 caracteres)

글坼가⑴감套擘終長①加⒈丟倘⓶增⓶가采⓶擘❷小終⓷丟❶長貶❷가掊貶插①增復合감不가終終

Isso espera a entrada como uma lista separada por espaço. Isso gera a resposta correta para 1 1e 2, mas por 2 1ou 3leva muito tempo, então desisti de esperar que ela terminasse.

Com comentários:

글坼 | split at spaces
가⑴ | iteration count = 0

감套 | while:
  擘終長①加⒈丟 | remove zeros from end and add to iteration count
  倘 | if the list is not empty:
    ⓶增⓶ | increment iteration count
    가采⓶擘❷小終⓷丟 | separate out active segment
    ❶長貶❷가掊貶插 | compute reduced active segment
    ①增復合 | repeat reduced active segment and concat
    감 | continue while loop
  不 | else
    가 | stop while loop
  終 | end if
終 | end while

2
Um link para um intérprete seria útil ... Além disso, 86 bytes, usando UTF-16?
— Peter Taylor

@ PeterTaylor: Obrigado, adicionou o link para o intérprete no artigo. E sim, 43 caracteres BMP são convertidos para 86 bytes em UTF-16.
— Timwi 23/01

5

k (83)

worm:{-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;|,/x)}

isso provavelmente pode ser ainda mais jogado, pois apenas implementa a recorrência de maneira bastante direta.

a função básica de evolução {x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}, é de 65 caracteres e usa alguns truques para parar de aumentar a idade em que o verme morre. o invólucro coage uma entrada de um único número inteiro para uma lista, reverte a entrada (é mais curto escrever a recorrência em termos de um worm revertido da sua notação), solicita o ponto de correção, seleciona a idade como a saída e ajusta o resultado para explicar a superação na última geração.

se eu fizer a coerção e a reversão manualmente, ele cai para 80 ( {-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;x)}).

alguns exemplos:

  worm 1 1 0
51
  worm 2
51
  worm 1 1
19

infelizmente, provavelmente não é de muita utilidade para o Maior Número Imprimível , exceto em um sentido muito teórico, pois é bastante lento, limitado a números inteiros de 64 bits e, provavelmente, não é particularmente eficiente em termos de memória.

em particular, worm 2 1e worm 3apenas agite (e provavelmente jogaria 'wsfull(sem memória) se eu os deixasse continuar).


Tentei executar seu programa com esse intérprete online , mas ele não mostra nenhuma saída. (O envio de um arquivo de texto com extensão .k deve chamar o intérprete K.) Você sabe o que pode ser feito para enviar a saída para o stdout?
— res

Parece que está executando o kona, um clone de código aberto do k3. Meu código está escrito em k4 e é improvável que seja compatível com k3. Você pode obter uma cópia gratuita por tempo limitado do q / k4 em kx.com/software-download.php ; depois disso, inicie o REPL, digite ` to switch from q` to ke cole meu código. Como alternativa, você pode salvar meu código em um arquivo com uma .kextensão e carregá-lo no intérprete.
— Aaron Davies

2

APL (Dyalog Unicode) , 52 bytes SBCS

Economizou 7 bytes graças a @ngn e @ Adám.

0{⍬≡⍵:⍺⋄n←⍺+1⋄0=⊃⍵:n∇1↓⍵⋄n∇∊(⊂1n/-∘1@1¨)@1⊆∘⍵⍳⍨⌊\⍵}⌽

Experimente online!

Explicação:

0{...}⌽    ⍝ A monadic function train. We define a recursive function with two
           ⍝ arguments: zero (our counter), and the reverse of our input
⍬≡⍵:⍺      ⍝ Our base case - if our input is an empty list, return our counter
n←⍺+1      ⍝ Define 'n' as our counter plus 1
0=⊃⍵:n∇1↓⍵ ⍝ If the first element of the input is zero, recurse with the tail
           ⍝ of our input and n
⌊\⍵        ⍝ Minimum-expand: creates a new list from our input where each element
           ⍝ is the incremental minimum     
⍳⍨         ⍝ Applies above to both sides of the index-of function. Index-of returns
           ⍝ the index of the first occurence of each element in the left-side list.
           ⍝ At this point, a (reversed) input list of [3 4 5 2 3 4] would result
           ⍝ in [1 1 1 4 4 4]
⊆∘⍵        ⍝ Partition, composed with our input. Partition creates sublists of the
           ⍝ right input whenever the integer list in the left input increases.
           ⍝ This means we now have a list of sub-lists, with the first element
           ⍝ being the worm's active segment.
(...)@1    ⍝ Take the active segment and apply the following function train...
-∘1@1¨     ⍝ Subtract 1 from the first element of the active segment
1n/        ⍝ Replicate the resultant list above n+1 times
⊂          ⍝ Enclose the above, so as to keep the original shape of our sub-array
∊          ⍝ Enlist everything above together - this recursively concatenates our
           ⍝ new active segment with the remainder of the list
n∇         ⍝ Recurse with the above and n

Imaginei que o APL teria uma solução realmente limpa para isso, não é uma linguagem baseada em array?
— ThePlasmaRailgun 13/03

1

Scala, 198

type A=List[Int]
def T(w:A)={def s(i:Int,l:A):Stream[A]=l match{case f::r=>l#::s(i+1,if(f<1)r
else{val(h,t)=l.span(_>=l(0));List.fill(i)(h(0)-1::h.tail).flatten++t})
case _=>Stream()};s(2,w).length}

Uso:

scala> T(List(2))
res0: Int = 51

1

K, 95

{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}

.

k)worm:{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}
k)worm 2
51
k)worm 1 1
19
q)worm 1 1 0 0 0 0
635

1

C (gcc) , 396 bytes

#define K malloc(8)
typedef*n;E(n e,n o){n s=K,t=s;for(*s=*o;o=o[1];*t=*o)t=t[1]=K;t[1]=e;e=s;}main(c,f,l,j,a)n*f;{n w=K,x=w;for(;l=--c;x=x[1]=K)*x=atoi(f[c]);for(;w&&++l;)if(*w){n v=K,z=v,u=w,t=K;for(a=*v=*w;(u=u[1])&&*u>=*w;*z=*u)z=z[1]=K;for(x=v[1],v=K,*v=a-1,1[u=v]=x;u;u=u[1])w=w[1];for(j=~l;j++;)u=t=E(t,v);for(;(u=u[1])&&(x=u[1])&&x[1];);u[1]=0;w=w?E(w,t):t;}else w=w[1];printf("%d",--l);}

Experimente online!

Sei que estou extremamente atrasado para a festa, mas pensei em tentar isso em C, o que exigia uma implementação de lista vinculada. Não é realmente um jogo de golfe, além de alterar todos os identificadores para caracteres únicos, mas funciona!

No geral, estou muito feliz considerando que este é o terceiro programa C / C ++ que eu já escrevi.


Você realmente precisa de uma lista vinculada? Por que não apenas alocar matrizes? Como este é um código de golfe, você nem precisa se preocupar em liberá-los quando terminar. Você pode até encontrar uma maneira de armazená-los na pilha de chamadas (não tenho certeza).
— dfeuer 16/03

Além disso, você não precisa de uma função principal. Basta escrever uma função que tome o worm como argumento e retorne sua vida útil. O worm pode ser uma matriz e seu comprimento, ou talvez uma matriz que termine em um número negativo.
— dfeuer 16/03

1

Haskell , 84 bytes

(0!).reverse
n!(x:y)|x<1=(n+1)!y|(a,b)<-span(>=x)y=(n+1)!(([-1..n]*>x-1:a)++b)
n!_=n

Experimente online!

Obrigado a @xnor por dois bytes.

Sinto que deve haver uma boa maneira de fatorar o incremento comum, mas ainda não encontrei um pequeno.


1
Dois pequenos tacos : verifique a caixa vazia da lista em segundo e nreduza para 1.
— xnor 16/03

Eu também acho que deveria haver uma maneira de não escrever (n+1)!duas vezes, mas minha tentativa só empatou.
— xnor 16/03


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.