Enumera i programmi Brainf ** k validi


41

Golunar / unario è un modo per codificare tutti i validi Brainfuck i programmi, ma non è un'enumerazione, poiché la maggior parte numeri naturali non corrispondono a un programma valido.

Ai fini di questa sfida, assumere un nastro doppiamente infinito e nessun commento, vale a dire che un programma Brainfuck è valido se e solo se è costituito esclusivamente da caratteri <>+-.,[]e tutte le parentesi destra e sinistra corrispondono.

Ad esempio, il programma vuoto ,[+][-]., [>+<[--].]e +[+[+][+[+]+]+]+.sono programmi Brainfuck validi, mentre ][e a[]non lo sono.

Compito

Scrivi un programma o una funzione che accetta un programma Brainfuck valido come input e restituisce un numero naturale ( 1 , 2 , 3 , ...), con i seguenti vincoli:

  • L'output generato deve essere diverso per tutti i programmi Brainfuck validi.

  • Per ogni numero naturale n , deve esistere un programma Brainfuck valido che, se fornito come input, genera l'output n .

Regole aggiuntive

  • Dato un programma Brainfuck di 100 o meno byte, il programma o la funzione deve terminare entro un minuto.

    Ciò significa che non è possibile scorrere su tutti i programmi Brainfuck validi fino a quando non si corrisponde all'input.

  • Si applicano le regole standard del .


3
Stavo pensando di codificarlo come ottale, ma le parentesi corrispondenti lo rendono difficile.
DankMemes,

Il programma vuoto è un programma Brainfuck valido? Deve essere mappato anche su un numero intero naturale?
orlp

9
Perché il voto stretto? Questa è una domanda affascinante e il problema sembra avere dimensioni e varietà per un grande golf.
trichoplax,

1
@orlp Sì, il programma vuoto soddisfa la definizione di cui sopra
Dennis

3
Aspetto ancora di vedere una risposta scritta in Brainfuck ...
Michael Hampton,

Risposte:


16

Python 3, 443 158 155 154 134 131 128 124 117 116 115 byte

c=d=C=D=0
for e in input():v='[<>,.-+]'.find(e);d=d*8+v;c+=c<0<6<v;c-=d>1>v;C,D=(c,C+1,d,D)[v>6::2]
print(-~D*8**C)

Diversi byte grazie a Sp3000 e Mitch Schwartz: D

Come funziona:

Questo mappa tutti i programmi BF validi in tutti i programmi BF possibili, validi o non validi, che non iniziano con un [, in un rapporto uno a uno. Successivamente, il nuovo programma viene semplicemente convertito in ottale.

Ecco la formula di mappatura:

  1. Separare un programma BF in 3 parti. La prima parte è il prefisso più grande costituito da soli [caratteri. La terza parte è il più grande postfisso composto da soli ]caratteri. La seconda parte è al centro.
  2. Smaltire la prima parte. Questi possono essere ricalcolati in seguito.
  3. Rimuovere tutte le ]parentesi nella terza parte che corrispondono alle [parentesi nella seconda parte. Questi possono anche essere ricalcolati in seguito.
  4. Concatenare la seconda e la terza parte insieme.

Se non capisci questa spiegazione, puoi trovare una spiegazione estesa nella chat a partire da qui .

Per riferimento, ecco i primi 20 programmi:

1 : 
2 : <
3 : >
4 : ,
5 : .
6 : -
7 : +
8 : []
9 : <[]
10 : <<
11 : <>
12 : <,
13 : <.
14 : <-
15 : <+
16 : [<]
17 : >[]
18 : ><
19 : >>
20 : >,

Ecco i primi 1000 programmi: http://pastebin.com/qykBWhmD
Ecco il programma che ho usato per generarli: http://ideone.com/e8oTVl

Ecco qui Hello, World!:

>>> ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
457711481836430915510337664562435564418569135809989841510260388418118348571803953323858180392373

Minimo cavillo: non è possibile mappare un programma su 0 .
Dennis,

@Dennis Tuttavia un programma vuoto conta come programma?
Decadimento beta


@Dennis Lo aggiusterò quando lo golf.
TheNumberOne,

3
Questo è geniale
orgoglioso haskeller il

13

Python 2, 157 byte

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x

Sembra ancora abbastanza giocabile, ma sto pubblicando questo per ora. Usa la ricorsione con un po 'di cache. In modo fastidioso, D.getnon corto circuito per la memorizzazione nella cache, quindi non posso salvare 9 byte in questo modo ...

La mappatura privilegia prima la lunghezza, quindi l'ordine lessicografico rispetto all'ordinamento "][><.-,+"(vedere gli esempi di output di seguito). L'idea principale è quella di confrontare i prefissi.

La variabile otiene traccia del numero di [parentesi ancora aperte per il prefisso corrente, mentre la variabile daccetta uno dei tre valori che indicano:

  • d = 1: Il prefisso attuale è lessicograficamente precedente a s. Aggiungi tutti i programmi con questo prefisso e lunghezza <= s,
  • d = -1: Il prefisso attuale è lessicograficamente più tardi di s. Aggiungi tutti i programmi con questo prefisso e lunghezza < s.
  • d = 0: Il prefisso corrente è un prefisso di s, quindi potremmo cambiare din 1 o -1 in seguito.

Ad esempio, se abbiamo s = "[-]"e il nostro prefisso attuale è p = "+", poiché pè più tardi che slessicograficamente sappiamo solo di aggiungere i programmi che iniziano con i pquali sono strettamente più brevi di s.

Per fare un esempio più dettagliato, supponiamo di avere un programma di input s = "-[]". La prima espansione ricorsiva fa questo:

  (o == 0)               # Adds a program shorter than s if it's valid
                         # For the first expansion, this is 1 for the empty program
+ f(s[1:], o=-1, d=1)    # ']', o goes down by one due to closing bracket
+ f(s[1:], o=1, d=1)     # '[', o goes up by one due to opening bracket
+ f(s[1:], o=0, d=1)     # '>'
+ f(s[1:], o=0, d=1)     # '<'
+ f(s[1:], o=0, d=1)     # '.', d is set to 1 for this and the previous branches
                         # since they are lexicographically earlier than s's first char
+ f(s[1:], o=0, d=0)     # '-', d is still 0 since this is equal to s's first char
+ f(s[1:], o=0, d=-1)    # ',', d is set to -1 for this and the later branches
                         # since they are lexicographically later than s's first char
+ f(s[1:], o=0, d=-1)    # '+'

Si noti come in realtà non utilizzare i prefissi nella ricorsione - tutti ci preoccupiamo per loro viene catturato attraverso le variabili d, oe il programma di input contrazione s. Noterai molte ripetizioni sopra: è qui che entra in gioco la cache, che ci consente di elaborare programmi da 100 caratteri entro il limite di tempo.

Quando sè vuoto, guardiamo (d>=0 and o==0), che decide se restituire 1 (conta questo programma perché è lessicograficamente in anticipo / uguale e il programma è valido), o 0 (non contare questo programma).

Qualsiasi situtazione con o < 0ritorna immediatamente 0, poiché tutti i programmi con questo prefisso hanno più ]s di [, e quindi non sono validi.


Le prime 20 uscite sono:

 1
> 2
< 3
. 4
- 5
, 6
+ 7
[] 8
>> 9
>< 10
>. 11
>- 12
>, 13
>+ 14
<> 15
<< 16
<. 17
<- 18
<, 19
<+ 20

Utilizzando lo stesso esempio Hello World della risposta di @ TheNumberOne:

>>> f("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.")
3465145076881283052460228065290888888678172704871007535700516169748342312215139431629577335423L

4

Python 2, 505 (non giocato a golf)

Mi è piaciuto sviluppare questo approccio, ma potrei non disturbarmi a giocare a golf perché non è competitivo rispetto ad altri approcci. Lo sto pubblicando per motivi di diversità e possibile interesse estetico. Implica la ricorsione e un po 'di matematica.

F={0:1}

def f(n):
    if n not in F:
        F[n]=6*f(n-1) + sum(f(i)*f(n-2-i) for i in range(n-1))

    return F[n]

def h(x):
    if x=='': return 0

    if len(x)==1: return '+-<>,.'.find(x)

    if x[0]!='[':
        return h(x[0]) * f(len(x)-1) + h(x[1:])

    d=i=1
    while d:
        if x[i]==']': d-=1
        elif x[i]=='[': d+=1
        i+=1

    a=i-2
    b=len(x)-i

    return 6*f(a+b+1) + sum(f(i)*f(a+b-i) for i in range(a)) + h(x[1:i-1]) * f(b) + h(x[i:])

def g(x):
    return sum(f(i) for i in range(len(x))) + h(x) + 1

print g(raw_input())

La funzione f(n)conta il numero di programmi brainfuck validi di lunghezza n. h(x)Mappe programmi di lunghezza na [0..f(n)-1], e g(x)è la funzione di rango biunivoca in questione.

L'idea principale è che un programma non vuoto può iniziare con [o con uno dei 6 non []caratteri. Nel primo caso, possiamo scorrere le possibili posizioni della corrispondenza ]e ricorrere sulla parte chiusa e sulla coda (dove coda indica la sottostringa che segue la ]). In quest'ultimo caso, possiamo ricorrere sulla coda (dove coda significa far cadere il primo personaggio). Questo ragionamento può essere utilizzato sia per il conteggio che per il rango di calcolo.

I programmi più brevi avranno sempre un rango inferiore rispetto ai programmi più lunghi e il modello di parentesi è un fattore determinante secondario. I non []caratteri sono ordinati in base a "+ - <> ,." (che è arbitrario).

Ad esempio con n=4abbiamo questi casi:

zxxx
[]xx
[x]x
[xx]

dove zsta per non []carattere e xsta per qualsiasi personaggio, con la limitazione che ]deve corrispondere all'iniziale [. I programmi sono classificati in base a tale ordine e ricorsivamente in base alle xsottosezioni, con la sezione sinistra prioritaria rispetto alla sezione destra negli ultimi casi. Il calcolo del rango è simile ai sistemi numerici a radice mista ed fè importante per calcolare l'attuale "radice".


4

Questa risposta è una prova formale per la risposta di TheNumberOne , enumera i programmi Brainf ** k validi , dove può essere un po 'difficile capire i punti fini del perché l'enumerazione è corretta. Non è banale capire perché non esiste un programma non valido che si associa a un numero non coperto da un programma valido.

In questa risposta le maiuscole vengono utilizzate per indicare i programmi e le variabili minuscole vengono utilizzate per funzioni e numeri interi. ~ è l'operatore di concatenazione.

Proposta 1:

Lascia che la funzione f sia il programma descritto in quella risposta. Quindi per ogni programma U esiste un programma V valido tale che f (U) = f (V)

Definizione 1:

Sia g (X) il numero di [quello che appare nel programma X, e sia h (X) il numero di ]quello che appare.

Definizione 2:

Definisci P (x) come questa funzione:

P(x) = "" (the empty program) when x <= 0
P(x) = "]" when x = 1
P(x) = "]]" when x = 2
etcetera

Definizione 3:

Dato un programma X, denota X1 come il suo più grande prefisso di [caratteri, X2 il suo centro e X3 il suo più grande suffisso di ]caratteri.

Prova della proposizione 1:

Se g (U) = h (U) allora U è un programma valido e possiamo prendere V = U. (caso banale).

Se g (U) <h (U) allora possiamo creare V anteponendo i [simboli n = h (U) - g (U) . Ovviamente f (V) = f (U) poiché tutti i [simboli nel prefisso vengono rimossi.

Ora considera g (U)> h (U). Definisci T = U2 ~ U3. se g (T) <= h (T), allora possiamo costruire V rimuovendo i [simboli n = g (U) - h (U) .

Quindi possiamo supporre che h (T) <g (T). Costruire V = T ~ P (g (T) - h (T)).

Abbiamo bisogno di tre piccoli fatti per procedere:

Rivendicazione 1: g (U2) = g (T)

U3 non contiene alcun [simbolo per definizione. Come T = U2 ~ U3, i suoi [simboli sono tutti nella prima parte.

Rivendicazione 2: h (U3) <g (T)

Ciò deriva dal notare che h (T) <g (T) e h (U3) <h (U3 ~ U2) = h (T).

Rivendicazione 3: h (V3) = g (U2) - h (U2)

h(V3) = h(U3) + g(T) - h(T)                           using the construction of V
h(V3) = h(U3) + g(U2) + g(U3) - h(U2) - h(U3)         apply the definition of T
h(V3) = g(U2) - h(U2) *one term cancels, g(U3)        is always zero, as U3 contains only `]` symbols*

Ora mostriamo che f (V) = f (U).

f(U) = U2 ~ P(h(U3) - g(U2)) = U2                     claim 2, definition of P

f(V) = U2 ~ P(h(V3) - g(V2))
     = U2 ~ P(h(V3) - g(U2))
     = U2 ~ P(g(U2) - h(U2) - g(U2))                  claim 3
     = U2 ~ P(-h(U2))
     = U2                                             definition P

Questo completa la prova. QED

Facciamo anche unicità.

Proposta 2:

Sia U, V due programmi diversi e validi. Quindi f (U)! = F (V)

Questo è abbastanza semplice rispetto alla proposta precedente.

Supponiamo che U2 = V2. Ma poi l'unico modo in cui U e V possono essere differenti è aggiungendo o rimuovendo n [e ]simboli rispettivamente a U1 e U3. Tuttavia, questo cambia l'output di f, poiché f conterà il numero di ]simboli senza eguali nel suffisso.

Quindi U2! = V2.

Ovviamente, questo porta a una contraddizione. Poiché U2 e V2 sono letteralmente contenuti nell'output di f (U) ef (V) rispettivamente, non possono differire, tranne che per il "bordo", il punto in cui U2 è concatenato con U3. Ma il primo e l'ultimo simbolo di U2 e V2 non possono essere [o ]per definizione, mentre quelli sono gli unici simboli consentiti rispettivamente in U1, U3, V1, V3 e rispettivamente. Quindi otteniamo U2 = V2. QED

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.