Õppetund
Teooria — Kahendotsing: neli valikut, kuusteist tsüklit
Kahendotsing leiab sorditud massiivist väärtuse x, säilitades indeksite lo kuni hi vahemiku, milles x peab olemasolu korral asuma. Iga läbim loeb keskmise elemendi a[mid] ja jätab alles selle poole, kus x paikneb. Vahemik on kinnine, kui hi on viimane sellesse kuuluv indeks (lo <= hi, hi algväärtus n − 1), ning poolavatud, kui hi on esimene indeks pärast vahemikku (lo < hi, hi algväärtus n).
Mida iga sümbol tähendab
lo, hi- veel läbiotsitava vahemiku otspunktid
mid- ⌊(lo + hi)/2⌋, sellel läbimil loetav indeks
n- elementide arv; indeksid on 0 kuni n − 1
N- pikim massiiv, mida hinnang katsetab: iga n vahemikus 0 kuni N ja iga sihtväärtus, kokku (N + 1)² juhtu
- Eeldab
- Massiivi väärtused on erinevad, tsükkel tagastab tulemuse kohe, kui a[mid] võrdub x-iga, ning käivituses ja hinnangus kasutatakse ülempiirita täisarve. Ületäitumise tabel loobub viimasest eeldusest ja arvutab 32 bitiga nagu Java.
- Ei kehti, kui
- Korduvate väärtuste korral tagastavad mõlemad õiged tsüklid endiselt indeksi, millel asub x, kuid mitte tingimata esimese sellise indeksi. Esimese leidmiseks, mida vajab sorditud indeksi vahemikupäring, tuleb võrdsuse korral jätkata: kasutada poolavatud vahemikku, määrata a[mid] ≥ x korral
hi = midning võtta tsükli lõppedes vastuseks lo.
Ülesanne täielikult lahendatud
-
Miks jäävad sõelale ainult kaks tsüklit 4 sammu
Vajuta Tsükkel takerdub, kui lo = mid. Kontroll suudab läbi proovida kuni 256 elemendiga massiivid, suuremaid mitte. Tõesta iga n-i korral, et paremalt lahtise vahemikuga tsükkel, kus
lo = mid + 1, lõpetab alati töö; et sama tsükkel omistusegalo = midtakerdub iga kord, kui hi = lo + 1 ja a[lo] < x; ning mida nõuab sama arutluskäik kinnise vahemikuga tsüklilt.-
Paremalt lahtise vahemikuga tsükli keha täidetakse ainult siis, kui lo < hi. Allapoole ümardatud mid ei ole sel juhul väiksem kui lo, kuid on rangelt väiksem kui hi.
-
Seega vähendab
hi = midväärtust hi vähemalt ühe võrra ninglo = mid + 1suurendab väärtust lo vähemalt ühe võrra. Mõlemal juhul väheneb vahe hi − lo, mille algväärtus on n, igal iteratsioonil vähemalt ühe võrra. Alla nulli see minna ei saa, järelikult lõpetab tsükkel hiljemalt n iteratsiooni järel. -
Vaatame nüüd omistust
lo = mid. Kui hi = lo + 1, siis mid = ⌊(2lo + 1)/2⌋ = lo. Kui a[lo] < x, jätab uuendus lo samaks ja järgmine iteratsioon kordab eelmist. Valitud näide jõuab sellesse seisu väärtustel lo = 6 ja hi = 7, kus a[6] = 13 ning x = 14. -
Kinnise vahemikuga tsükkel töötab seni, kuni lo <= hi, seega on lubatud ka lo = hi. Siis mid = lo = hi ning piirile väärtuse mid omistamine ei muudaks midagi. Mõlemad piiride uuendused peavad mid-ist mööduma; seda saab teha, sest a[mid] on juba loetud. Paremalt lahtine vahemik ei sisalda väärtust hi. Seal eemaldab
hi = midäsja loetud elemendi ja kõik sellest paremale jäävad elemendid, mis on tingimuse a[mid] > x põhjal välistatud. Omistushi = mid − 1eemaldaks lisaks elemendi a[mid − 1], kuigi just seal võib olla x.
Vastus
Iga iteratsioon peab otsinguvahemikku kahandama, kõrvale võib jätta ainult indeksid, millel x asuda ei saa, ning algne vahemik peab hõlmama kogu massiivi. Tingimuse
lo <= hikorral tähendab see valikuid hi = n − 1,hi = mid − 1jalo = mid + 1. Tingimuselo < hikorral tuleb valida hi = n,hi = midjalo = mid + 1. Kontroll kinnitab neid kaht toimivat varianti kuni 256 elemendiga massiividel. Arutluskäik kehtib aga iga n-i korral, mida ükski test tõestada ei saa. 32-bitises aritmeetikas on vaja veel eeldust, et lo + hi mahub arvutüüpi; kolmas tähelepanek näitab, mis juhtub ilma selle eelduseta. -
Õpitee
Miks on algoritm õige
Allikad (2)
- The overflow in the JDK's binary search, the nine years it went unnoticed, and Bentley's proved-correct version carrying the same line; also the source of the 1946 and 1962 dates the first insight quotes from Programming Pearls: Joshua Bloch, "Extra, Extra - Read All About It: Nearly All Binary Searches and Mergesorts are Broken." Google Research Blog, 2 June 2006.
- The bug report the page reproduces: java.util.Arrays.binarySearch on a 1,100,000,000-byte array, searching for its last element, throwing ArrayIndexOutOfBoundsException: -1064671149: OpenJDK bug JDK-5045582, "(coll) binarySearch() fails for size larger than 1<<30." Filed 11 May 2004 against Java 5.0, resolved (fixed in 6) 28 April 2006.