Enumera alberi binari


20

Alberi binari

Un albero binario è un albero con nodi di tre tipi:

  • nodi terminali, che non hanno figli
  • nodi unari, che hanno un figlio ciascuno
  • nodi binari, che hanno due figli ciascuno

Possiamo rappresentarli con la seguente grammatica, data in BNF (forma Backus – Naur):

<e> ::= 
      <terminal>   
    | <unary>
    | <binary>

<terminal> ::= 
    "0"

<unary> ::= 
    "(1" <e> ")"

<binary> ::= 
    "(2" <e> " " <e> ")"

In questa grammatica i nodi sono indicati in preordine e ogni nodo è rappresentato da una cifra che è il numero di figli che ha.

Numeri di Motzkin

I numeri di Motzkin ( OEIS ) ( Wikipedia ) hanno molte interpretazioni, ma una interpretazione è che il nnumero di Motzkin è il numero di alberi binari distinti con nnodi. Inizia una tabella di numeri Motzkin

N          Motzkin number M(N)
1          1
2          1
3          2 
4          4 
5          9 
6         21 
7         51 
8        127 
    ...

per esempio M(5)è 9, e sono i nove distinti alberi binari con 5 nodi

1      (1 (1 (1 (1 0))))  
2      (1 (1 (2 0 0)))  
3      (1 (2 0 (1 0)))  
4      (1 (2 (1 0) 0))  
5      (2 0 (1 (1 0)))  
6      (2 0 (2 0 0))  
7      (2 (1 0) (1 0))  
8      (2 (1 (1 0)) 0)  
9      (2 (2 0 0) 0)  

Compito

Prendi un singolo intero positivo ncome input e output tutti i distinti alberi binari con nnodi.

Esempi nda 1 a 5 con parentesi inclusa per leggibilità

0

(1 0)

(1 (1 0))
(2 0 0)

(1 (1 (1 0)))
(1 (2 0 0))
(2 0 (1 0))
(2 (1 0) 0)

(1 (1 (1 (1 0))))
(1 (1 (2 0 0)))
(1 (2 0 (1 0)))
(1 (2 (1 0) 0))
(2 0 (1 (1 0)))
(2 0 (2 0 0))
(2 (1 0) (1 0))
(2 (1 (1 0)) 0)
(2 (2 0 0) 0)

Ingresso

L'input sarà un numero intero positivo.

Produzione

L'output dovrebbe essere una rappresentazione intelligibile degli alberi binari distinti con quel numero di nodi. Non è obbligatorio utilizzare la stringa esatta fornita dalla grammatica BNF sopra: è sufficiente che la sintassi utilizzata dia una rappresentazione inequivocabile degli alberi. Ad esempio, è possibile utilizzare []anziché ()un livello aggiuntivo di parentesi [[]]anziché [], parentesi esterne presenti o mancanti, virgole aggiuntive o senza virgole, spazi aggiuntivi, parentesi o nessuna parentesi, ecc.

Tutti questi sono equivalenti:

(1 (2 (1 0) 0))  
[1 [2 [1 0] 0]]  
1 2 1 0 0  
12100  
(1 [2 (1 0) 0])  
.:.--  
*%*55  
(- (+ (- 1) 1))
-+-11

Anche una variazione proposta da @xnor in un commento. Poiché esiste un modo per tradurlo in un formato comprensibile, è accettabile.

[[[]][]]  is (2 (1 0) 0)

Per rendere questo più facile da capire convertire alcuni dei []a ()piacere in modo

[([])()]

Ora se inizi con

[]

quindi inserire un file binario che richiede due espressioni ottenute

 [()()] which is 2

e quindi per il primo () inserisci un unario che necessita di un'espressione che ottieni

 [([])()] which is 21

ma poiché []o ()senza parentesi interne può rappresentare 0 che non necessita di più espressioni, è possibile interpretarlo come

 2100

Si noti che le risposte dovrebbero funzionare teoricamente con memoria infinita, ma ovviamente esauriranno la memoria per un input finito dipendente dall'implementazione.

Variazioni di output

BNF             xnor       Christian   Ben
b(t, b(t, t))   [{}{{}{}}] (0(00))     (1, -1, 1, -1)                         
b(t, u(u(t)))   [{}{(())}] (0((0)))    (1, -1, 0, 0)           
b(u(t), u(t))   [{()}{()}] ((0)(0))    (1, 0, -1, 0)                     
b(b(t, t), t)   [{{}{}}{}] ((00)0)     (1, 1, -1, -1)              
b(u(u(t)), t)   [{(())}{}] (((0))0)    (1, 0, 0, -1)                          
u(b(t, u(t)))   [({}{()})] ((0(0)))    (0, 1, -1, 0)                          
u(b(u(t), t))   [({()}{})] (((0)0))    (0, 1, 0, -1)                        
u(u(b(t, t)))   [(({}{}))] (((00)))    (0, 0, 1, -1)                          
u(u(u(u(t))))   [(((())))] ((((0))))   (0, 0, 0, 0)  

Un possibile posto per verificare la presenza di alberi duplicati

Un posto per verificare la presenza di un duplicato è con M (5).
Questo albero è stato generato due volte per M (5) da M (4) alberi

(2 (1 0) (1 0))  

il primo aggiungendo un ramo unario a

(2 (1 0) 0)

e in secondo luogo aggiungendo un ramo unario a

(2 0 (1 0))

Capire BNF

BNF è composto da semplici regole:

<symbol> ::= expression

dove a sinistra è racchiuso un nome simbolo <>.
Sulla destra c'è l'espressione per costruire il simbolo. Alcune regole usano altre regole nella costruzione, ad es

<e> ::= <terminal>

e può essere un terminal

e alcune regole hanno caratteri usati nella costruzione del simbolo, ad es

<terminal> ::= "0"

terminal è solo il carattere zero.

Alcune regole hanno diversi modi per costruirle, ad es

<e> ::= 
      <terminal>   
    | <unary>
    | <binary>

An epuò essere a <terminal>o a <unary>o a <binary>.

E alcune regole sono una sequenza di parti, ad es

<unary> ::= "(1" <e> ")"

A unarysono i personaggi (1seguiti da ciò che può essere costruito per eseguito da ).

Si inizia sempre con la regola di partenza, che per questo <e>.

Alcuni semplici esempi:

La sequenza più semplice è giusta 0. Quindi iniziamo con la regola di partenza <e>e vediamo che ci sono tre scelte:

  <terminal>   
| <unary>
| <binary>

quindi prendi il primo <terminal>. Ora un terminale non ha scelta ed è 0. Quindi sostituisci <terminal>con 0nella <e>regola e il gioco è fatto.

Quindi il prossimo è (1 0). Inizia con <e>e usa la regola <unary>che ha

"(1" <e> ")"

Ora questo ha bisogno di un <e>così torniamo a <e>e facciamo una scelta di uno dei tre, questa volta scegliendo, <terminal>che dà 0. Sostituzione 0in (1 <e> )(1 0), e questo viene sostituito in <unary>modo <e>è (1 0).


Quindi, un albero binario? "un albero binario è una struttura di dati ad albero in cui ogni nodo ha al massimo due figli"
f 14nɛtɪk

3
La tua descrizione è quella di un albero binario. Gli alberi binari non devono avere 2 figli. Significa solo che hanno al massimo 2 bambini. Immagino che unary-binary sia solo un termine più specifico che in realtà non significa nulla di diverso.
fəˈnɛtɪk,

Considera di chiarire cos'è "BNF" per quelli di noi che non sono informatici
Luis Mendo

1
@GuyCoder Il mio punto è, se qualcuno vede "BNF" e non sa cosa significa che potrebbero essere rimandati e smettere di leggere. Forse usare il nome anziché l'acronimo e aggiungere un collegamento a Wikipedia sarebbe sufficiente
Luis Mendo

4
@ mbomb007 Nome modificato. Dovrei ottenere un premio alla pari. :)
Guy Coder

Risposte:


12

Haskell, 68 byte

t 0=[""]
t 1=["0"]
t n=['(':x++y++")"|k<-[1..n-1],x<-t k,y<-t$n-k-1]

I nodi terminali sono rappresentati da nodi 0unari e binari da (e)resp. (ee), quindi i due alberi a tre nodi sono indicati come (00)e ((0)).

Esempi:

*Main> t 5
["(0(00))","(0((0)))","((0)(0))","((00)0)","(((0))0)","((0(0)))","(((0)0))","(((00)))","((((0))))"]
*Main> length $ t 8
127
*Main> length $ t 15
113634 

5

CJam (37 byte)

0aa{_2m*2\f+1Y$f+++:e__&}qi:A*{,A=},p

Demo online . Nota che questo non è molto efficiente e probabilmente non vorrai provare a calcolare l'input 5online.

Dissezione da seguire.


5

Pyth ( 24 21 19 byte)

Questo si basa sulla mia soluzione Python 3 .

f!|sTf<sY0._T^}1_1t

È la prima volta che utilizzo Pyth, quindi probabilmente è ancora giocabile a golf.

Esempio , output quando input è 4:

[[1, 0, -1], [1, -1, 0], [0, 1, -1], [0, 0, 0]]

1 rappresenta un nodo binario, 0 rappresenta un nodo unario e -1 rappresenta un nodo terminale. C'è un nodo terminale implicito alla fine di ogni albero.

Spiegazione :

f!|sTf<sY0._T^}1_1t
f                    filter
             ^    t  length n-1 lists of elements
              }1_1   from [1, 0, -1]
 !|                  for when both
   sT                sum of list is 0, and
     f    ._T        for each prefix of list,
      <sY0           sum of prefix is non-negative.


4

Brainfuck, 107 byte

,>++>-[-[<-[<-[>>[[>+<-]<]>+>[[<+>>>>>+<<<<-]>]>>++>,++++>]>[<+>-[+>>]>[<->[.<<<
<<]+[->+]+>>>]]]]<[[,<]<]<]

formattato:

,>++>-
[
  -
  [
    <-
    [
      <-
      [
        >>
        [[>+<-]<]
        >+>
        [[<+> >>>>+<<<<-]>]
        >>++>,++++>
      ]
      >
      [
        <+>-
        [
          +>>
        ]
        >
        [
          <->[.<<<<<]
          +[->+]
          +>>>
        ]
      ]
    ]
  ]
  <
  [
    [,<]
    <
  ]
  <
]

Provalo online

L'input viene preso come byte e l'albero 12100viene rappresentato come \x01\x02\x03\x02: per riconvertire, tradurre tr/\x01\x02\x03/012/, invertire la stringa e aggiungere un finale 0. Gli alberi sono separati da \xfe. (L'output può essere reso più semplice da leggere, ad esempio cambiando il primo -in -36e il .in +47.-47, dove -36significa una stringa di 36 -caratteri, ecc.)

Questo approccio si avvale della proprietà (utilizzata anche da Ben Frankel): considerando i possibili nodi -1, 0, 1e ignorando il -1nodo finale , un elenco rappresenta un albero valido se e solo se (1) tutti i prefissi dell'elenco hanno una somma non negativa, e (2) la somma dell'intero elenco è uguale 0. La prima condizione viene mantenuta durante la generazione di nodi intermedi, quindi solo la seconda condizione deve essere verificata alla fine.

Il nastro è diviso in celle a 5 nodi,

i d x 0 0

dove si itrova l'indice (decrescente da sinistra a destra), dè la somma parziale ed xè l'elemento.

Schizzo del flusso di controllo:

take n and push initial node
while stack is non-empty:
    if rightmost node can be decremented:
        decrement rightmost node
        if there are less than n nodes:
            push new node
        else if valid tree:
            print
    else:
        backtrack (pop)

Si noti che a volte un valore viene archiviato o inizializzato come uno o due più grande del valore effettivo (concettuale) e regolato secondo necessità.


3

Python 3 ( 138 134 128 121 119 byte)

from itertools import*
lambda n:[any(sum(t[:k])<0for k in range(n))|sum(t)or print(t)for t in product(*[[-1,0,1]]*~-n)]

Esempio di output, per n=5:

(0, 0, 0, 0)
(0, 0, 1, -1)
(0, 1, -1, 0)
(0, 1, 0, -1)
(1, -1, 0, 0)
(1, -1, 1, -1)
(1, 0, -1, 0)
(1, 0, 0, -1)
(1, 1, -1, -1)

1 rappresenta un nodo binario, 0 rappresenta un nodo unario e -1 rappresenta un nodo terminale. C'è un nodo terminale implicito alla fine di ogni albero.

Il programma inizia a richiedere troppo tempo n=17.


3

JavaScript (Firefox 30-57), 79 byte

f=(m,l=0)=>m?[for(n of[1,0,-1])if(l>n&l<=m+n)for(a of f(m-1,l-n))[...a,n]]:[[]]

Dove -1rappresenta un terminale, 0un nodo unario e 1un nodo binario. Inizia a rallentare m=14sul mio PC. Funziona in modo ricorsivo dalla fine dell'albero.

  • Il numero di nodi non contabilizzati lè limitato dal fatto che alla fine può rimanere solo 1 nodo.
  • Il tipo del nodo successivo nè limitato dalla necessità di avere abbastanza nodi non contabilizzati per essere suoi figli.

2

Prolog, 149 144 138 137 131 107 byte

e(L,L)-->[0].

e([_|A],L)--> 
    [1],
    e(A,L).

e([_,_|A],L)--> 
    [2],
    e(A,B), 
    e(B,L).

e(M,E):-                   
    length([_|L],M),        
    e(L,[],E,[]).           

?- e(5,S).
S = [1, 1, 1, 1, 0] ;
S = [1, 1, 2, 0, 0] ;
S = [1, 2, 0, 1, 0] ;
S = [1, 2, 1, 0, 0] ;
S = [2, 0, 1, 1, 0] ;
S = [2, 0, 2, 0, 0] ;
S = [2, 1, 0, 1, 0] ;
S = [2, 1, 1, 0, 0] ;
S = [2, 2, 0, 0, 0].

E contare le soluzioni

e_count(N,Count) :-
    length([_|Ls], N),
    findall(., phrase(e(Ls,[]),E), Sols),
    length(Sols, Count).

?- e_count(N,Count).
N = Count, Count = 1 ;
N = 2, Count = 1 ;
N = 3, Count = 2 ;
N = Count, Count = 4 ;
N = 5, Count = 9 ;
N = 6, Count = 21 ;
N = 7, Count = 51 ;
N = 8, Count = 127 ;
N = 9, Count = 323 ;
N = 10, Count = 835 ;
N = 11, Count = 2188 ;
N = 12, Count = 5798 ;
N = 13, Count = 15511 ;
N = 14, Count = 41835 ;
N = 15, Count = 113634 

1

Python, 71 byte

f=lambda n:{(a+b,)for k in range(n)for a in f(k)for b in f(n+~k)}or[()]

Questo rappresenta alberi come tuple nidificate come ((((),), ()),), che possono essere trasformati ((())())rimuovendo virgole, spazi e il più esterno ().

Una versione precedente di 76 byte:

f=lambda n:{'('+a+b+')'for k in range(n)for a in f(k)for b in f(n+~k)}or['']

1

CJam, 38 byte

Utilizza un approccio diverso a cui risponde CJam di Peter Taylor.

3rim*{:(1\+[{1$+}*])\:(_:z#|!},

L'output sarà qualcosa di simile 1110120020102100. Ogni albero è un gruppo di ncifre (dove si ntrova il numero di input).

L'idea di base è che generiamo ogni possibile stringa di cifre 0, 1e 2, quindi filtrare solo quelli che sono alberi ben formate.

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.