Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 6 / 15 Dvejetainė paieška ~55 min
Kodas

Kraštiniai atvejai — čia ji ir lūžta

Sukurk search_test.go (žr. dešinėje). Šitą testą rašom mes; tavo darbas — kad LowerBound jį praeitų.

Laimingas atvejis — „įrašas kažkur viduryje" — yra lengvas. Visos toliau išvardytos vietos yra ten, kur ranka rašyta dvejetainė paieška iš tikrųjų lūžta.

Dešimt kraštinių atvejų

$ go test -run TestLowerBoundBoundaries -v .
=== RUN   TestLowerBoundBoundaries/exact_match,_first
=== RUN   TestLowerBoundBoundaries/exact_match,_middle
=== RUN   TestLowerBoundBoundaries/exact_match,_last
=== RUN   TestLowerBoundBoundaries/between_two_items
=== RUN   TestLowerBoundBoundaries/before_everything
=== RUN   TestLowerBoundBoundaries/AFTER_everything
=== RUN   TestLowerBoundBoundaries/empty_slice
=== RUN   TestLowerBoundBoundaries/single_item,_before
=== RUN   TestLowerBoundBoundaries/single_item,_exact
=== RUN   TestLowerBoundBoundaries/single_item,_after
--- PASS: TestLowerBoundBoundaries (0.00s)

Trys iš jų verti atskiro žodžio.

„AFTER everything" grąžina 4, kai bibliotekoje 4 įrašai. Indeksas, kurio nėra. Būtent todėl hi pradedamas nuo len(items), o ne nuo len(items)-1: atsakymas „už galo" turi turėti kur tilpti. Pradėk nuo len-1 ir šis atvejis tyliai grąžins paskutinį įrašą.

Tuščias slice grąžina 0 ir neišvis nepatenka į ciklą, nes lo < hi iškart neteisinga. Nulinis atvejis pasitvarko pats — jei invariantas teisingas.

Vieno įrašo biblioteka tikrinama tris kartus, nes joje mid visada 0 ir klaida su mid + 1 čia virsta amžinuoju ciklu.

Dublikatai: kurį iš trijų?

lib := sorted("a", "b", "b", "b", "c")
LowerBound(lib, "b")  // → 1

Trys „b". Teisingas atsakymas — pirmasis, indeksas 1. Tai ir reiškia lower bound.

Jei tavo paieška grąžina 2 arba 3, ji atrodo teisinga ir praeis paviršutinišką patikrinimą. Bet kodas, kuris nuo rezultato eina pirmyn rinkdamas visus sutampančius įrašus, praleis dalį jų — ir klaida išlįs kur nors kitur, visiškai kitoje vietoje.

transit savo paieškos semantiką irgi testuoja, o ne prielaidauja.

Ir svarbiausias testas

$ go test -run TestUnsortedInputSilentlyLies -v .
    search_test.go:93: unsorted input: linear says 0, binary says 2 —
                       binary is wrong, and silent

Nesurikiuotas įvedimas dvejetainei paieškai nėra klaida. Ji nepraneša nieko. Ji tiesiog grąžina neteisingą skaičių.

Spąstai

Surikiuota tvarka yra prielaida, o ne rekomendacija. Tiesinė paieška veikia visada; dvejetainė veikia tik tada, kai jos sąlyga išpildyta, ir pati to netikrina — patikrinimas kainuotų O(n) ir sunaikintų visą prasmę. Prielaidas saugo tavo programa, ne paieška.