Menghitung pohon biner

20

Pohon biner

Pohon biner adalah pohon dengan simpul dari tiga jenis:

  • terminal node, yang tidak memiliki anak
  • node unary, yang masing-masing memiliki satu anak
  • node biner, yang masing-masing memiliki dua anak

Kita dapat mewakili mereka dengan tata bahasa berikut, diberikan dalam BNF (bentuk Backus-Naur):

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

<terminal> ::= 
    "0"

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

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

Dalam tata bahasa ini, node diberikan dalam preorder dan setiap node diwakili oleh digit yang merupakan jumlah anak yang dimilikinya.

Nomor Motzkin

Angka Motzkin ( OEIS ) ( Wikipedia ) memiliki banyak interpretasi, tetapi satu interpretasi adalah bahwa angka nMotzkin adalah jumlah pohon biner yang berbeda dengan nnode. Tabel nomor Motzkin dimulai

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

misalnya M(5)adalah 9, dan sembilan pohon biner yang berbeda dengan 5 node

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)  

Tugas

Ambil bilangan bulat positif tunggal nsebagai input dan output semua pohon biner yang berbeda dengan nnode.

Contoh untuk ndari 1 hingga 5 dengan tanda kurung dimasukkan untuk keterbacaan

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)

Memasukkan

Input akan menjadi satu bilangan bulat positif.

Keluaran

Outputnya harus berupa representasi yang jelas dari pohon biner yang berbeda dengan banyak node. Tidaklah wajib untuk menggunakan string persis yang diberikan oleh tata bahasa BNF di atas: cukup bahwa sintaks yang digunakan memberikan representasi pohon yang jelas. Misalnya, Anda dapat menggunakan []alih-alih (), tanda kurung tambahan [[]]alih-alih [], tanda kurung luar ada atau tidak ada, koma ekstra atau tanpa koma, ruang ekstra, tanda kurung atau tanpa tanda kurung, dll.

Semua ini setara:

(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

Juga variasi yang ditujukan oleh @xnor dalam komentar. Karena ada cara untuk menerjemahkan ini ke format yang dapat dipahami, maka itu dapat diterima.

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

Untuk membuat ini lebih mudah untuk memahami mengkonversi beberapa []untuk ()suka begitu

[([])()]

Sekarang jika Anda mulai dengan

[]

kemudian masukkan biner yang membutuhkan dua ekspresi yang Anda dapatkan

 [()()] which is 2

dan kemudian untuk yang pertama () masukkan unary yang membutuhkan satu ekspresi yang Anda dapatkan

 [([])()] which is 21

tetapi karena []atau ()tanpa tanda kurung bagian dalam dapat mewakili 0 yang tidak memerlukan ekspresi lagi Anda dapat menafsirkannya sebagai

 2100

Perhatikan bahwa jawaban harus bekerja secara teoritis dengan memori tak terbatas, tetapi jelas akan kehabisan memori untuk input terbatas yang bergantung pada implementasi.

Variasi keluaran

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)  

Tempat yang memungkinkan untuk memeriksa duplikat pohon

Satu tempat untuk memeriksa duplikat adalah dengan M (5).
Pohon yang satu ini dihasilkan dua kali untuk M (5) dari pohon M (4)

(2 (1 0) (1 0))  

yang pertama dengan menambahkan cabang unary ke

(2 (1 0) 0)

dan kedua dengan menambahkan cabang unary ke

(2 0 (1 0))

Memahami BNF

BNF terdiri dari aturan sederhana:

<symbol> ::= expression

di mana di sebelah kiri adalah nama simbol yang dikelilingi oleh <>.
Di sebelah kanan adalah ekspresi untuk membangun simbol. Beberapa aturan menggunakan aturan lain dalam konstruksi, misalnya

<e> ::= <terminal>

e bisa menjadi terminal

dan beberapa aturan memiliki karakter yang digunakan dalam membangun simbol, misalnya

<terminal> ::= "0"

terminal hanya karakter nol.

Beberapa aturan memiliki beberapa cara untuk membuatnya, misalnya

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

An edapat berupa a <terminal>atau a <unary>atau a <binary>.

Dan beberapa aturan adalah urutan bagian, misalnya

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

A unaryadalah karakter yang (1diikuti oleh apa yang dapat dibangun untuk ediikuti oleh ).

Anda selalu mulai dengan aturan awal, yang untuk ini <e>.

Beberapa contoh sederhana:

Urutan paling sederhana adalah adil 0. Jadi kita mulai dengan aturan awal <e>dan melihat bahwa ada tiga pilihan:

  <terminal>   
| <unary>
| <binary>

jadi ambil yang pertama <terminal>. Sekarang terminal tidak memiliki pilihan dan sedang 0. Jadi ganti <terminal>dengan 0di <e>aturan dan Anda selesai.

Lalu yang berikutnya adalah (1 0). Mulai dengan <e>dan gunakan aturan <unary>yang ada

"(1" <e> ")"

Sekarang ini perlu <e>jadi kami kembali ke <e>dan memilih salah satu dari tiga, kali ini memilih, <terminal>yang memberi 0. Mengganti 0menjadi (1 <e> )memberi (1 0), dan ini diganti menjadi <unary>apa <e>adanya (1 0).

Guy Coder
sumber
Jadi, pohon biner? "pohon biner adalah struktur data pohon di mana setiap simpul memiliki paling banyak dua anak"
fəˈnɛtɪk
3
Deskripsi Anda adalah tentang pohon biner. Pohon biner tidak perlu memiliki 2 anak. Itu berarti mereka memiliki paling banyak 2 anak. Saya kira unary-binary hanyalah istilah yang lebih spesifik yang tidak benar-benar berarti sesuatu yang berbeda.
fəˈnɛtɪk
Pertimbangkan untuk menjelaskan apa itu "BNF" bagi kita yang bukan ilmuwan komputer
Luis Mendo
1
@GuyCoder Maksud saya adalah, jika seseorang melihat "BNF" dan tidak tahu apa artinya mereka mungkin ditunda dan berhenti membaca. Mungkin menggunakan nama dan bukan akronim dan menambahkan tautan ke Wikipedia sudah cukup
Luis Mendo
4
@ mbomb007 Nama diubah. Saya harus mendapatkan penghargaan dari teman sebaya untuk itu. :)
Guy Coder

Jawaban:

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]

Terminal node diwakili oleh 0, node unary dan binary oleh (e)resp. (ee), jadi dua pohon tiga simpul diberikan sebagai (00)dan ((0)).

Contoh:

*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 
Sievers Kristen
sumber
5

CJam (37 byte)

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

Demo online . Perhatikan bahwa ini sangat tidak efisien, dan Anda mungkin tidak ingin mencoba menghitung input 5online.

Diseksi untuk diikuti.

Peter Taylor
sumber
5

Pyth ( 24 21 19 bytes)

Ini didasarkan pada solusi Python 3 saya .

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

Ini pertama kalinya saya menggunakan Pyth jadi ini kemungkinan masih golf.

Contoh , output ketika input adalah 4:

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

1 mewakili simpul biner, 0 mewakili simpul unary, dan -1 mewakili simpul terminal. Ada simpul terminal tersirat di ujung setiap pohon.

Penjelasan :

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.
Ben Frankel
sumber
Yang menarik: Kode sumber Pyth
Guy Coder
4

brainfuck, 107 byte

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

Diformat:

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

Cobalah online

Input diambil sebagai byte , dan hierarki 12100direpresentasikan sebagai \x01\x02\x03\x02: untuk mengkonversi kembali, menerjemahkan tr/\x01\x02\x03/012/, membalikkan string, dan menambahkan final 0. Pohon dipisahkan oleh \xfe. (Keluaran dapat dibuat lebih mudah dibaca dengan misalnya mengubah yang pertama -menjadi -36dan yang .menjadi +47.-47, di mana -36berarti string 36 -karakter, dll.)

Pendekatan ini memanfaatkan properti (yang juga digunakan Ben Frankel): mempertimbangkan kemungkinan simpul sebagai -1, 0, 1dan mengabaikan -1simpul terakhir , daftar mewakili pohon yang valid jika dan hanya jika (1) semua awalan daftar memiliki jumlah non-negatif, dan (2) jumlah seluruh daftar sama dengan 0. Kondisi pertama dipertahankan sementara menghasilkan node perantara, jadi hanya kondisi kedua yang perlu diperiksa di akhir.

Rekaman ini dibagi menjadi sel 5-node,

i d x 0 0

di mana iindeks (turun dari kiri ke kanan), dadalah jumlah parsial, dan xmerupakan elemen.

Sketsa aliran kontrol:

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)

Perhatikan bahwa kadang-kadang nilai disimpan atau diinisialisasi sebagai satu atau dua lebih besar dari nilai aktual (konseptual) dan disesuaikan sesuai kebutuhan.

Mitch Schwartz
sumber
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)]

Contoh output, untuk 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 mewakili simpul biner, 0 mewakili simpul unary, dan -1 mewakili simpul terminal. Ada simpul terminal tersirat di ujung setiap pohon.

Program mulai memakan waktu terlalu lama n=17.

Ben Frankel
sumber
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]]:[[]]

Dimana -1mewakili terminal, 0simpul unary dan 1simpul biner. Mulai lambat di m=14PC saya. Secara rekursif bekerja kembali dari ujung pohon.

  • Jumlah node yang tidak dihitung ldibatasi oleh fakta bahwa mungkin hanya ada 1 simpul yang tersisa di akhir.
  • Jenis simpul selanjutnya ndibatasi oleh kebutuhan untuk memiliki cukup banyak simpul yang tidak terhitung untuk menjadi anak-anaknya.
Neil
sumber
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].

Dan untuk menghitung solusinya

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 
Guy Coder
sumber
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[()]

Ini mewakili pohon sebagai tupel bersarang seperti ((((),), ()),), yang dapat ditransformasikan menjadi ((())())dengan menghilangkan koma, spasi, dan bagian terluar ().

Versi 76 byte sebelumnya:

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

CJam, 38 byte

Menggunakan pendekatan berbeda yang dijawab Peter Taylor CJam.

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

Output akan menjadi sesuatu seperti 1110120020102100. Setiap pohon adalah sekelompok ndigit (di mana nadalah nomor input).

Ide dasarnya adalah bahwa kita menghasilkan setiap string mungkin digit 0, 1, dan 2, dan kemudian menyaring hanya orang-orang yang pohon well-formed.

Buah Esolanging
sumber