Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 11 / 15 Dvejetainiai medžiai ir BST ~50 min
Teorija

Ką už tvarką sumoki

Ta pati biblioteka, tie patys 10 000 klausimų, trys struktūros:

$ algo tree -in s10k.jsonl
n = 10000 distinct titles

  tree height      30   (log2 n = 13.3)
  in order?        true

                      cmps/lookup     build cmps  lookup time
  tree                     16.703         157025      3.108ms
  hash (lesson 10)          1.310           7198      1.001ms
  linear (lesson 1)         5000.5              0        125ms

  build time: tree 4ms, hash 2.501ms

  keys in sorted order: tree yes (walk), hash no (must sort all 10000)

Medis pralaimi maišos lentelei 12.8 karto vieninteliam klausimui „ar yra toks pavadinimas". Pastatymas kainuoja 157 025 palyginimų prieš 7 198 — dvidešimt du kartus daugiau.

Tai reikia pasakyti tiesiai, o ne apeiti. Jei tavo programai reikia tik tikslaus rakto paieškos, maišos lentelė yra teisingas pasirinkimas ir medis būtų klaida.

Kur dingsta tie 16.7

Aukštis 30, o log2(10000) yra 13.3. Medis daugiau nei dvigubai gilesnis už idealų.

Taip ir turi būti. Nė vienas mazgas nesirūpina pusiausvyra — medis įgauna tokią formą, kokią jam duoda įterpimo tvarka, o atsitiktinė tvarka duoda kreivoką, bet ne katastrofišką. Pamatuota:

$ for n in 100 1000 10000 100000; do algo gen -n $n -seed 7; algo tree; done

        n   height   log2 n    cmps/lookup
      100       13      6.6          7.710
     1000       22     10.0         12.273
    10000       30     13.3         16.703
   100000       41     16.6         21.367

Šimteriopai padidinus n nuo 1 000 iki 100 000, kaina paaugo nuo 12.3 iki 21.4 — mažiau nei dvigubai. Tai ir yra logaritmas: augimas yra, bet jis nyksta.

Padalyk stulpelius vienas iš kito ir pamatysi pastovią daugybą:

n aukštis ÷ log2 n palyginimai ÷ log2 n
100 1.97 1.17
1 000 2.20 1.23
10 000 2.26 1.26
100 000 2.47 1.29

Ne 1.0, bet ir ne augantis dydis. Medis yra maždaug dvigubai gilesnis už idealų ir tokiu lieka — būtent tai reiškia „O(log n) su blogesne konstanta".

Maišos lentelės stulpelis toje pačioje lentelėje būtų buvęs 1.31 visur, be jokio log n.

Atmintis: pamatuok, o ne spėk

Medžio mazgas turi dvi rodykles ten, kur grandinėlės įrašas turėjo vieną:

Item         56 baitų
entry (10)   80 baitų  (raktas + įrašas + 1 rodyklė)
node  (11)   88 baitų  (raktas + įrašas + 2 rodyklės)

Aštuoni baitai mazgui — atrodo, medis brangesnis. Bet suskaičiuok visą struktūrą prie n = 10 000:

maišos: 10 000 × 80 B  +  16 384 kibirų × 8 B  =  931 072 B
medis:  10 000 × 88 B                          =  880 000 B

Bendrai medis sunaudoja mažiau. Nes maišos lentelė moka ne tik už įrašus — ji moka ir už kibirų masyvą, o tas masyvas ir yra tai, kas jai perka trumpas grandinėles. Prie apkrovos 0.61 tuščių kibirų yra 8 925, ir jie visi užima vietą.

Vienam mazgui medis brangesnis; visai struktūrai — ne. Skirtumas priklauso nuo apkrovos koeficiento, kurį pasirinkai dešimtoje pamokoje.

Sandoris, dviem eilutėmis

Maišos lentelė: 1.3 palyginimo vienam klausimui, ir vienas klausimas. Medis: 16.7 palyginimo, ir šeši klausimai.

Antrasis stulpelis nėra „taip pat, tik lėčiau". Jis yra kitas produktas.