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←⍺+10=⊃⍵:n1↓⍵⋄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=⊃⍵:n1↓⍵  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.