Vita di un verme


28

condizioni

Un worm è un elenco di numeri interi non negativi e il suo elemento più a destra (ovvero l' ultimo ) è chiamato head . Se la testa non è 0, il worm ha un segmento attivo costituito dal blocco contiguo più lungo di elementi che include la testa e ha tutti i suoi elementi almeno grandi quanto la testa . Il segmento attivo ridotto è il segmento attivo con la testa decrementata di 1. Ad esempio, il worm 3 1 2 3 2ha il segmento attivo 2 3 2e il segmento attivo ridotto lo è 2 3 1.

Regole dell'evoluzione

Un worm si evolve passo dopo passo come segue:

Nel passaggio t (= 1, 2, 3, ...),
    se la testa è 0: elimina la testa
    altrimenti: sostituisce il segmento attivo con t + 1 copie concatenate del segmento attivo ridotto.

Fatto : qualsiasi worm alla fine si evolve in un elenco vuoto e il numero di passaggi per farlo è la durata del worm .

(I dettagli possono essere trovati in The Worm Principle , un articolo di LD Beklemishev. L'uso di "list" per indicare una sequenza finita, e "head" per indicare il suo ultimo elemento, è preso da questo documento - non dovrebbe essere confuso con l'uso comune degli elenchi come tipo di dati astratto , dove head di solito indica il primo elemento.)

Esempi (segmento attivo tra parentesi)

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

Le vite dei vermi sono in genere enormi, come mostrato dai seguenti limiti inferiori in termini della gerarchia standard delle funzioni in rapida crescita 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)

Sorprendentemente, worm [3] ha già una vita che supera di gran lunga il numero di 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

Scrivere il sottoprogramma della funzione più breve possibile con il seguente comportamento:

Input : qualsiasi worm.
Output : la durata del worm.

La dimensione del codice è misurata in byte.


Ecco un esempio (Python, golf a circa 167 byte):

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) è la durata del worm [n], allora il tasso di crescita di t (n) è approssimativamente quello della funzione di Goodstein . Quindi, se questo può essere giocato a meno di 100 byte, potrebbe anche dare una risposta vincente alla domanda stampabile con il numero più grande . (Per questa risposta, il tasso di crescita potrebbe essere notevolmente accelerato avviando sempre il contapassi su n - lo stesso valore del worm [n] - invece di avviarlo su 0.)


Sono confuso dal tuo codice. Hai detto che la testa è l' elemento più a destra , ma nel tuo esempio Python, tratti la testa come un w[0]elemento * più a sinistra di quella lista?

@LegoStormtroopr Se riesci a considerare un elenco con sinistra e destra. Se consideri solo un primo e l'ultimo, puoi mappare il primo a destra o il primo o l'ultimo durante la lettura della stringa iniziale, che non fa parte della domanda. Ma neanche gli input delle funzioni sono stati definiti in modo rigoroso.
Bob,

@LegoStormtroopr - Buona cattura; Ho corretto il codice aggiungendo una riga per invertire il worm di input, la cui testa dovrebbe essere effettivamente sulla destra (ovvero l'ultimo elemento nell'elenco w). È per efficienza che il programma opera sul worm invertito.
res

Ottenere la giusta risposta per 2 1potrebbe essere troppo chiedere in un tempo ragionevole, ma un test utile è che la sequenza dovrebbe iniziare (2 1), 2 0 2 0, 2 0 (2), 2 0 (1 1 1 1), ...
Peter Taylor

1
@ThePlasmaRailgun - Per parafrasare Harvey Friedman, i numeri derivati ​​dalle funzioni al livello ε_0 nella gerarchia in rapida crescita (come le vite dei vermi) sono completamente INDIPENDIBILI rispetto a TREE (3) .
res

Risposte:


15

GolfScript ( 56 54 caratteri)

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

Demo online

Penso che il trucco chiave qui sia probabilmente quello di mantenere il worm in ordine inverso. Ciò significa che è abbastanza compatto trovare la lunghezza del segmento attivo: .0+.({<}+??(dove 0viene aggiunto come protezione per garantire che troviamo un elemento più piccolo della testa).


A parte questo, alcune analisi sulla durata della vita del verme. Io indicare il worm come age, head tail(cioè in ordine inverso rispetto la notazione della domanda) con esponenti per indicare la ripetizione in testa e la coda: per esempio 2^3è 2 2 2.

Lemma : per ogni segmento attivo xs, esiste una funzione f_xsche si age, xs 0 tailtrasforma in f_xs(age), tail.

Dimostrazione: nessun segmento attivo può mai contenere un 0, quindi l'età per quando cancelliamo tutto prima che la coda sia indipendente dalla coda e quindi è solo una funzione di xs.

Lemma : per ogni segmento attivo xs, il verme age, xsmuore all'età f_xs(age) - 1.

Prova: dal precedente lemma, si age, xs 0trasforma in f_xs(age), []. Il passaggio finale è l'eliminazione di ciò 0, che non viene toccato in precedenza perché non può mai far parte di un segmento attivo.

Con questi due lemmati, possiamo studiare alcuni semplici segmenti attivi.

Per 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

così f_{1^n} = x -> f_{1^{n-1}}^{x+1}(x+2)(con il caso base f_{[]} = x -> x+1, o se preferisci f_{1} = x -> 2x+3). Vediamo f_{1^n}(x) ~ A(n+1, x)dove si Atrova la funzione Ackermann – Péter.

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

È abbastanza per capire 1 2( 2 1nella notazione della domanda):

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, []

Quindi dato l'input 2 1prevediamo l'output ~ 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 davvero iniziare a capire perché questa funzione cresce così follemente.


Molto bello. Penso che questo programma fornirà anche una risposta vincente al programma di chiusura più breve la cui dimensione di output supera il numero di Graham . (L'attuale vincitore ha 63 byte di codice Haskell.) Ad esempio, a 55 byte, qualcosa del tipo (dato che sono incline a errori di sintassi) 9{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L~calcola la durata del worm [9], che supera di gran lunga il numero di Graham - e può essere golf ulteriormente.
res

9

GolfScript, 69 62 caratteri

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

La funzione Cprevede che il worm sia nello stack e lo sostituisce con il risultato.

Esempi:

> [1 1]
19

> [2]
51

> [1 1 0]
51

Fantastico! Sicuramente puoi modificarlo un po 'per dare anche un vincitore definitivo alla domanda "Numero più grande stampabile" .
res

Non ti ho visto pubblicare nulla laggiù, quindi sono andato avanti e ho pubblicato una modifica di questo codice come quella che credo sia la risposta vincente finora, supponendo che *e ^non vengano utilizzati come operatori aritmetici di moltiplicare ed esponenziale. Certamente, se vuoi inviare lì la tua (senza dubbio superiore) risposta, rimuoverò felicemente la mia.
ris.

7

Rubino - 131 caratteri

So che questo non può competere con le soluzioni GolfScript sopra e sono abbastanza sicuro che questo possa ridurre un punteggio o più caratteri, ma onestamente sono felice di essere stato in grado di risolvere il problema senza essere stato. Grande puzzle!

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}

La mia soluzione pre-golfata da cui deriva quanto sopra:

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

Suggerimento generico: molti problemi di golf funzionano su numeri interi non negativi, nel qual caso si if foo==0può tagliare if foo<1. Questo può farti risparmiare un personaggio qui.
Peter Taylor,

Per inciso, trovo affascinante che questo funzioni senza un secondo reverse.
Peter Taylor,

Ah no. Funziona solo sui casi di test perché hanno solo segmenti attivi palindromici.
Peter Taylor,

Grazie per il consiglio di golf, @PeterTaylor. Inoltre, buona presa sul secondo rovescio mancante. L'ho aggiunto. Proverò a riscriverlo in un altro modo senza usare il contrario più tardi. Sono abbastanza sicuro di riuscire a mettere la elseclausola su una riga e quindi scambiare la if..else..endfrase ternaria. Potrei anche usare una lambda per salvare alcuni personaggi, credo.
OI

6

Sclipting (43 caratteri)

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

Ciò prevede l'input come un elenco separato da spazi. Questo produce la risposta corretta per 1 1e 2, ma per 2 1o 3impiega troppo tempo, quindi ho rinunciato ad aspettare che finisse.

Con commento:

글坼 | 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
Un collegamento a un interprete sarebbe utile ... Inoltre, 86 byte, usando UTF-16?
Peter Taylor,

@PeterTaylor: Grazie, aggiunto il link all'interprete per l'articolo. E sì, 43 caratteri BMP si traducono in 86 byte in UTF-16.
Timwi,

5

k (83)

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

questo può probabilmente essere ulteriormente approfondito, in quanto implementa la ricorrenza in modo abbastanza semplice.

la funzione di evoluzione di base {x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}, è di 65 caratteri e usa alcuni trucchi per smettere di aumentare l'età quando il worm muore. il wrapper costringe un input di un singolo intero a un elenco, inverte l'input (è più breve scrivere la ricorrenza in termini di un worm invertito dalla tua notazione), chiede il punto di correzione, seleziona l'età come output e regola il risultato per tenere conto del superamento dell'ultima generazione.

se eseguo manualmente la coercizione e l'inversione, scende a 80 ( {-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;x)}).

qualche esempio:

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

sfortunatamente, probabilmente non è molto utile per il numero più grande stampabile , tranne in senso molto teorico, in quanto è piuttosto lento, limitato a numeri interi a 64 bit e probabilmente non particolarmente efficiente in termini di memoria.

in particolare, worm 2 1e worm 3solo agitarsi (e probabilmente getterebbe 'wsfull(senza memoria) se glielo lascio andare avanti).


Ho provato a eseguire il tuo programma con questo interprete online , ma non mostra alcun output. (L'invio di un file di testo con estensione .k dovrebbe invocare l'interprete K.) Sai cosa si potrebbe fare per inviare l'output a stdout?
res

Sembra che stia eseguendo kona, un clone open source di k3. Il mio codice è scritto in k4 ed è improbabile che sia compatibile con k3. Puoi ottenere una copia gratuita a tempo limitato di q / k4 su kx.com/software-download.php ; una volta che lo hai fatto, avvia REPL, digita ` to switch from q` in ke incolla il mio codice. In alternativa, puoi salvare il mio codice in un file con .kestensione e caricarlo nell'interprete.
Aaron Davies il

2

APL (Dyalog Unicode) , SBCS da 52 byte

7 byte salvati grazie a @ngn e @ Adám.

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

Provalo online!

Spiegazione:

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

Ho pensato che APL avrebbe una soluzione davvero pulita per questo, non è un linguaggio basato su array?
ThePlasmaRailgun

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 byte

#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);}

Provalo online!

So di essere ESTREMAMENTE in ritardo alla festa, ma ho pensato di cimentarmi in questo in C, che ha richiesto un'implementazione dell'elenco collegato. Oltre a cambiare tutti gli identificatori in singoli personaggi, non è proprio un vero golf, ma funziona!

Tutto sommato, sono abbastanza felice considerando che questo è il terzo programma C / C ++ che abbia mai scritto.


Hai davvero bisogno di un elenco collegato? Perché non solo allocare array? Dato che si tratta di code golf, non hai nemmeno bisogno di preoccuparti di liberarli quando hai finito. Si potrebbe anche essere in grado di trovare un modo per memorizzarli sul stack di chiamate (non sono sicuro).
Dfeuer

Inoltre, non hai bisogno di una funzione principale. Basta scrivere una funzione che prende il worm come argomento e restituisce la sua durata. Il worm può essere un array e la sua lunghezza, o forse un array che termina con un numero negativo.
Dfeuer

1

Haskell , 84 byte

(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

Provalo online!

Grazie a @xnor per due byte.

Sento che dovrebbe esserci un buon modo per calcolare l'incremento comune, ma non ne ho ancora trovato uno breve.


1
Due piccoli campi da golf : controlla il secondo caso dell'elenco vuoto e n
spostati in

Penso anche che ci dovrebbe essere un modo per non scrivere (n+1)!due volte, ma il mio tentativo è solo legato.
xnor


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.