Calcoli della tabella di laver e un algoritmo che non è noto terminare in ZFC


12

Le tabelle Laver forniscono esempi di programmi che non hanno dimostrato di terminare nel sistema assiomatico standard della matematica ZFC ma che terminano quando si assumono assiomi cardinali molto grandi.

introduzione

I classici tavoli Laver sono le algebre finite uniche con set sottostante e un'operazione che soddisfa l'identità e dove per e dove .An{1,...,2n}*x * (y * z)=(x * y) * (x * z)x*1=x+1x<2n2n*1=1

Maggiori informazioni sulle classiche tabelle Laver sono disponibili nel libro Trecce e autodistribuzione di Patrick Dehornoy.

Sfida

Qual è il codice più breve (in byte) che calcola 1*32nelle classiche tabelle Laver e termina esattamente quando trova un ncon ? In altre parole, il programma termina se e solo se trova un con ma per il resto funziona per sempre.1*32<2nn1*32<2n

Motivazione

Un cardinale rango-in-rango (chiamato anche un cardinale I3) è un livello estremamente grande di infinito e se uno assume l'esistenza di un cardinale rango-in-rango, allora è in grado di dimostrare più teoremi che se non lo fa presumere l'esistenza di un cardinale di rango in rango. Se esiste un cardinale di rango in rango, allora c'è un tavolo Laver classico dove . Tuttavia, non esiste alcuna prova nota che in ZFC. Inoltre, è noto che l'almeno dove è maggiore di (che è un numero estremamente elevato poiché la funzione Ackermann è una funzione rapida crescita). Pertanto, tale programma durerà per un periodo di tempo estremamente lungo.An1*32<2n1*32<2nn1*32<2nAck(9,Ack(8,Ack(8,254)))Ack

Voglio vedere quanto breve può essere scritto un programma in modo che non sappiamo se il programma termina usando il sistema assiomatico standard ZFC ma dove sappiamo che il programma alla fine termina in un sistema assiomatico molto più forte, vale a dire ZFC + I3. Questa domanda è stata ispirata dal recente post di Scott Aaronson in cui Aaronson e Adam Yedidia hanno costruito una macchina di Turing con meno di 8000 stati in modo tale che ZFC non può dimostrare che la macchina di Turing non si interrompe ma è noto che non termina quando si assumono grandi ipotesi cardinali.

Come vengono calcolate le classiche tabelle Laver

Quando si calcolano le tabelle Laver, di solito è conveniente utilizzare il fatto che in algebra , abbiamo tutto compreso .An2n * x=xxAn

Il codice seguente calcola la classica tabella Laver An

# table (n, x, y) restituisce x * y in A n
Tabella: = function (n, x, y)
se x = 2 ^ n restituisce y;
elif y = 1 quindi restituisce x + 1;
else restituisce table (n, table (n, x, y-1), x + 1); fi; fine;

Ad esempio, l'input table(4,1,2)tornerà 12.

Il codice per table(n,x,y)è piuttosto inefficiente e può essere calcolato nella tabella Laver solo in un ragionevole lasso di tempo. Fortunatamente, ci sono algoritmi molto più veloci per il calcolo delle classiche tabelle Laver rispetto a quelli sopra indicati.A4


2
Benvenuti in PPCG! Ottimo post!
NoOneIsHere

1
Secondo Wikipedia, Ack (9, Ack (8, Ack (8.254))) è un limite inferiore su n per il quale il periodo supera 16. Per questo, possiamo controllare 1 * 16 anziché 1 * 32. Modificherò di conseguenza il mio programma.
John Tromp,

1
Ho iniziato a scrivere una macchina Turing per fare questo, e penso di aver individuato un errore out-by-factor-of-two. Dougherty non ha dimostrato che Ack(9,Ack(8,Ack(8,254)))è un limite inferiore per la prima tabella in cui la prima riga ha il periodo 32, ovvero dove 1*16 < 2^n?
Peter Taylor,

1
Se hai una macchina a 2 simboli con 20 stati per Ackermann, per favore, dammi un link, perché probabilmente posso rubare alcune idee da esso. Ho 44 stati da calcolare table(n,x,y)e penso che ci vorranno tra 25 e 30 stati per impostare le costanti e il ciclo esterno. L'unica rappresentazione di TM diretta che posso trovare su esolangs.org è esolangs.org/wiki/ScripTur e non è poi così da golf.
Peter Taylor,

1
cheddarmonk.org/papers/laver.pdf è quanto mi aspetto di fare questa settimana, perché viaggerò .
Peter Taylor,

Risposte:


4

Calcolo lambda binario, 215 bit (27 byte)

\io. let
  zero = \f\x. x;
  one = \x. x;
  two = \f\x. f (f x);
  sixteen = (\x. x x x) two;
  pred = \n\f\x. n (\g\h. h (g f)) (\h. x) (\x. x);
  laver = \mx.
    let laver = \b\a. a (\_. mx (b (laver (pred a))) zero) b
    in laver;
  sweet = sixteen;
  dblp1 = \n\f\x. n f (n f (f x)); -- map n to 2*n+1
  go2 = \mx. laver mx sweet mx (\_. mx) (go2 (dblp1 mx));
in go2 one

compila a (utilizzando il software su https://github.com/tromp/AIT )

000101000110100000010101010100011010000000010110000101111110011110010111
110111100000010101111100000011001110111100011000100000101100100010110101
00000011100111010100011001011101100000010111101100101111011001110100010

Questa soluzione è principalmente dovuta a https://github.com/int-e


2
Non sono sicuro di come hai ottenuto il tuo punteggio, ma per impostazione predefinita gli invii dovrebbero essere assegnati in base al numero di byte nel codice. Conto 375 byte per questo invio. È inoltre necessario includere il nome della lingua e facoltativamente un collegamento a un interprete per la lingua.
Alex A.

Probabilmente dovresti includere il codice esatto della lunghezza di 234 bit nel tuo post.
CalculatorFeline

2
La codifica è disponibile su Wikipedia . C'è anche un link a questo interprete (non testato). Questi dovrebbero essere controllati, tuttavia, e la codifica binaria dovrebbe essere anche nel post.
PurkkaKoodari,

1
Per i linguaggi compilati, calcoliamo il codice scritto dall'utente, non il numero di byte nel binario generato.
Alex A.

5
@AlexA. Non è necessario ... qualsiasi forma di codice che può essere compresa da un compilatore o da un interprete va bene.
feersum

4

CJam ( 36 32 byte)

1{2*31TW$,(+a{0X$j@(@jj}2j)W$=}g

In pratica, questo errore si esaurisce abbastanza rapidamente perché trabocca lo stack di chiamate, ma su una macchina teorica illimitata è corretto, e capisco che sia il presupposto di questa domanda.

Il codice per table(n,x,y)è piuttosto inefficiente e può essere calcolato nella tabella Laver A 4 in un tempo ragionevole.

in realtà non è corretto se memorizziamo nella cache valori calcolati per evitare di ricalcolarli. Questo è l'approccio che ho adottato usando l' joperatore (memoisation) . Mette alla prova A 6 in millisecondi e trabocca lo stack test A 7 - e in realtà ho optato table per il golf.

Dissezione

Se assumiamo che nsia compreso dal contesto, anziché

f(x,y) =
    x==2^n ? y :
    y==1 ? x+1 :
    f(f(x,y-1),x+1)

possiamo rimuovere il primo caso speciale, dando

f(x,y) =
    y==1 ? x+1 :
    f(f(x,y-1),x+1)

e funziona ancora perché

f(2^n, 1) = 2^n + 1 = 1

e per ogni altro y,

f(2^n, y) = f(f(2^n, y-1), 1) = f(2^n, y-1) + 1

quindi per induzione otteniamo f(2^n, y) = y.

Per CJam risulta essere più conveniente invertire l'ordine dei parametri. E piuttosto che usare l'intervallo, 1 .. 2^nsto usando l'intervallo 0 .. 2^n - 1diminuendo ogni valore, quindi la funzione ricorsiva che sto implementando è

g(y,x) =
    y==0 ? x+1
         : g(x+1, g(y-1, x))

1           e# Initial value of 2^n
{           e# do-while loop
  2*        e#   Double 2^n (i.e. increment n)
  31T       e#   table(n,1,32) is g(31,0) so push 31 0
  W$,(+a    e#   Set up a lookup table for g(0,x) = x+1 % 2^n
  {         e#   Memoisation function body: stack is 2^n ... y x
    0X$j    e#     Compute g(0,x) = x+1 % 2^n
            e#     Stack is 2^n ... y x (x+1%2^n)
    @(      e#     Bring y to top, decrement (guaranteed not to underflow)
            e#     Stack is 2^n ... x (x+1%2^n) (y-1%2^n)
    @jj     e#     Rotate and apply memoised function twice: g(x+1,g(y-1,x))
  }
  2j        e#   Memoise two-parameter function
            e#   Stack: 2^n g(31,0)
  )W$=      e#   Test whether g(31,0)+1 is 2^n
}g          e# Loop while true

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.