Nu aveti permisiuni pentru a descarca fisierul grader_test3.in
Diferente pentru blog/cautare-binara intre reviziile #64 si #33
Diferente intre titluri:
Cautarea ta binara estegresita
Cautarea ta binara e gresita
Diferente intre continut:
Cautarea binara esteprintreprimiialgorimidivideandconquerstudiati la informatica.Algoritmulrezolva problema gasirii unui element x in un sir sortat A folosind monotoniaelementelor pentrua injumatatila fiecare passpatiuldecautare.Ideea algoritmului esimpla,insaaproape fiecare concurent olimpiada de informatica are cate o poveste cum apierdutpuncte lao problema din cauzaimplementarii.Majoritateastudentilorde informaticasi chiar doctoranzilor,dupacum nespuneJonBentleyinProgramming Pearls,nu reusesc sa scrie o cautare binara fara probleme.
Cautarea binara e probabil primul algoritm studiat de elevii sau studentii la informatica care exemplifica tehnica divide and conquer. Ea rezolva problema gasirii unui element x in un sir sortat A. Ideea de baza e simpla: folosindu-ne de monotonia datelor putem reduce la fiecare pas problema la jumatate. Totusi aproape fiecare concurent olimpiada de informatica are cate o poveste cum a bushit o problema din cauza unei cautari binare. La fel majoritatea programatorilor ce termina facultatea de informatica nu reusesc sa scrie o cautare binara fara probleme.
Implementarile pot avea *multe buguri* in zone cum ar fi:
*Buguri frecvente:* O implementare poate avea gramada de probleme cum ar fi:
*intrareciclu infinit
* ciclu infinit
* conditii gresite de terminare
* siruri scurte de lungime 0, 1, 2 * siruri in care x nu exista sau x apare de mai multe ori *siruriincarex apare in apropiere de inceput sau final
* probleme siruri de scurte de lungime 0, 1, 2 * probleme pentru siruri in care x nu exista sau x apare de mai multe ori * probleme cand x apare in apropiere de inceputul sau finalul sirului
*Optimizari premature*
Am vazut tot felul de variante, de exemplu unii testeaza daca a[mid] e egal cu x si scurt circuiteaza cautarea. Aceasta optimizare nu ajuta in cazul general, doar complica codul. Alta varianta esa reducimai mult problema folosind hi = mid - 1 sau lo = mid + 1.Optimizareae mica si adauga un pas logic in plus la care trebuie sa fiiatent.Cazurile la una din marginile sirului pot deveni mai dificilesau putem avea probleme de genul hi devine mai mic decat lo.
Am vazut tot felul de variante, de exemplu unii testeaza daca a[mid] e egal cu x si scurt circuiteaza cautarea. Aceasta optimizare nu ajuta in cazul general, doar complica codul. Alta varianta e ca poti reduce ceva mai mult problema folosind hi = mid - 1 sau lo = mid + 1. Ai un pas logic in plus la care trebuie sa fi atent. Pe langa asta cazurile in care ajungem la una din marginile sirului pot deveni mai dificile.
Multe implementari sunt "blindate" ca să evite bug-urile de mai sus. Problema e că lumea le blindează cu cod duplicat si error prone, repetand conditii. *Variante ale problemei* Exista versiuni diferite cum ar fi gasirea primei sau ultimei aparitii a lui x in sirul sortat, gasirea predecesorului sau succesorului valorii x in sir.
*Variante:* Problema poate aparea in versiuni diferite cu ar fi gasirea primei sau ultimei aparitii a lui x in sirul sortat, gasirea predecesorului sau succesorului valorii x in sir. Astfel ar fi utila o metoda care poate fi adaptata usor la astfel de cerinte.
*O solutieisteata*folosita de membrii infoarenautilizeazaputerile lui 2.
O solutie folosita frecvent de membrii infoarena foloseste puterile lui 2. Codul e elegant:
== code(c) |
int binary_search(int[]A, int x) {
int binary_search(int A, int x) {
int i, step, N = A.length; for (step = 1; step < N; step <<= 1); for (i = 0; step; step >>= 1) if (i + step < N && A[i + step] <= x) i += step;
if (A[i] == x) return i; else return -1;
return i;
} ==
Aceasta varianta deimplementareeste elegantainsamienuimiplace foarte tare.Dezavantajulprincipal estelizibilitateacodului.
Mie nu imi place aceasta varianta. Un dezavantaj e ca un programator are nu stie trucul intelege codul de mai sus mai greu. Nu am incercat sa vad cat de flexibila e solutia pentru variantele problemei de care vorbeam mai sus.
*Cum implementezisimplu, corect,clar si flexibil o cautare binara*
*Cum sa implementezi o cautare binara corecta:*
Folosim un *invariant* in bucla cautarii binare, adica o asertiune care e adevarata de fiecare data cand intram in bucla. Pentru cazul nostru acest invariant e ca lo indica spre un element care e mai mic ca x sau spre -1 si hi indica spre un element mai mare sau egal cu x sauspreA.length. Pe scurt <tex>A[lo] < x \le A[hi] </tex> (consideram <tex>A[-1] = -\infty</tex> si <tex>A[A.length] = +\infty</tex>)
Folosim un *invariant* in bucla cautarii binare, adica o asertiune care e adevarata de fiecare data cand intram in bucla. Pentru cazul nostru acest invariant e ca lo indica spre un element care e mai mic ca x sau spre -1 si hi indica spre un element mai mare sau egal cu x sau in A.length. Pe scurt <tex>A[lo] < x \le A[hi] </tex> (consideram <tex>A[-1] = -\infty</tex> si <tex>A[A.length] = +\infty</tex>)
Sa vedem cum arata codul: == code(c) |
intbinary_search(int[] A, int x) {
int search(int[] A, int x) {
int hi = A.length, lo = -1, mid; while (hi - lo > 1) { mid = (lo + hi) / 2; if (A[mid] < x)
lo = mid;
low = mid;
else hi = mid; }
* linia 2: setam pe hi si lo inafara sirului, astfel invariantul e indeplinit si nu trebuie sa tratam cazuri speciale. * linia 3: conditia de continuare a buclei e hi - lo > 1. Invariantul ales face ca hi si lo sa fie tot timpul distincte. La fiecare pas distanta intre hi si lo se injumatateste, iar cand hi si lo ajung consecutive ca pozitii in sir putem lua o decizie. * linia 4: mid va fi tot timpul intre lo si hi.
* linia6: stim ca A[mid] < x si astfel facand atribuirea lo = mid micsoram spatiu de cautare si pastraminvariantul * la linia8stim ca A[mid] >= x si putem face atribuirea hi = mid. * la linia10vedem daca x e in sir
* linia x: stim ca A[mid] < x si astfel facand atribuirea lo = mid micsoram spatiu de cautare si pastra invariantul * la linia y stim ca A[mid] >= x si putem face atribuirea hi = mid. * la linia z vedem daca x e in sir
** in caz afirmativ putem sa returnam indexul hi ** un caz negativ e cand ultimul element din sir e mai mic decat x. Atunci avem lo = A.length - 1 si hi = A.length ** celalalt caz negativ e ca hi sa fie undeva in interiorul sirului si sa avem ca A[lo] < x < A[hi]
Am scapat de greselile ce le mentionam mai sus, pentru ca invariantul ne demonstreaza corectitudinea algoritmului nostru. Ideea e foarte flexibila, putem schimba usor invariantul pentru a aborda variantele problemei mentionate. De exemplu pentru a gasi ultima pozitie din sir mai mica decat x putem folosi invariantul <tex>A[lo] \le x < A[hi]</tex>
Folosind un invariant am demonstrat corectitudinea cautarii.
Aceastaabordarea estedetaliata incarteaProgramming PearlsdeJonBentleype carev-orecomand.
Ideea e foarte flexibila, putem schimba usor invariantul pentru a aborda variantele problemei mentionate mai sus. De exemplu pentru a gasi ultima pozitie din sir mai mica decat x putem folosi invariantul <tex>A[lo] \le x < A[hi]</tex>
*Linkbaituldin titlu:)*Daca nu v-am convins de afirmatia din titlu, va mai zic ca, in 2006, Joshua Bloch, cel care a scris algoritmul de cautare binara in java.util.Arrays, a 'descoperit un bug':http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html in implementare. Acest bug aparea in majoritatea cautarilor binare sau a sortarilor prin interclasare scrise in ultimii 20 de ani. Lucrand la Google el a ajuns sa sorteze siruri de doua miliarde de numere. La pasul mid = (lo + hi) / 2 s-a depasit Integer.MAX_VALUE care e 2147483647 si codul a declansat o exceptie. Putem rezolva bugul folosind <tex>mid = lo + (hi - lo) / 2</tex> in loc de <tex>mid = (hi + lo) / 2</tex>.Stiu ca “You can’t teach an old dog new tricks”dar sper ca v-am convinsde utilitatea invariantilor.
Aceasta abordare e detaliata in cartea Programming Pearls de Jon Bentley. *Liknbaitul din titlu :)*
_Despre cautare binara pe numererealesau metoda bisectiei in episodul urmator:)._
Daca v-a sarit in ochi afirmatia din titlu, va mai zic ca in 2006, Joshua Bloch, cel care a scris algoritmul de cautare binara in java.util.Arrays a 'descoperit un bug':http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html in implementare. Acest bug care aparea in majoritatea cautarilor binare sau a sortarilor prin interclasare scrise in ultimii 20 de ani. Lucrand la Google el a ajuns sa sorteze siruri de doua miliarde de numere. Astfel pasul mid = (lo + hi) / 2 a ajuns sa depaseasca Integer.MAX_VALUE care e 2147483647. Putem rezolva bugul folosind <tex>mid = lo + (hi - lo) / 2</tex> in loc de <tex>mid = (hi + lo) / 2</tex>.
*Intrebari:* Voi ati avut vreodata probleme cu cautarile binare? Ce varianta folositi?
In urmatorul articol voi discuta ce probleme pot aparea la cautarea binara pe numere reale sau metoda bisectiei cum mai e numita. Voi ati avut vreodata probleme cu cautarile binare? Ce varianta folositi?
Diferente intre securitate:
protected
private
Diferente intre topic forum:
6864