Bine ati venit

În acest articol, veți afla cum funcționează algoritmul Binary Search în culise și cum îl puteți implementa în Python.

În special, veți învăța:

  • Cum funcționează algoritmul din culise pentru a găsi un element țintă.
  • Cum funcționează implementarea sa Python linie cu linie.
  • De ce este un algoritm foarte eficient în comparație cu Căutarea liniară.
  • Avantajele și cerințele sale.

Sa incepem! ✨

Acest algoritm este utilizat pentru a găsi un element într-o secvență ordonată (de exemplu: o listă, un tuplu sau un șir).

Cerințe

Pentru a aplica algoritmul Binary Search unei secvențe, secvența trebuie să fie deja sortată în ordine crescătoare. În caz contrar, algoritmul nu va găsi răspunsul corect. Dacă o va face, va fi prin pură coincidență.

ad-banner

💡 Sfat: Puteți sorta secvența înainte de a aplica Binary Search cu un algoritm de sortare care să răspundă nevoilor dvs.

Intrare și ieșire

Algoritmul (implementat ca funcție) are nevoie de aceste date:

  • O secvență ordonată de elemente (de exemplu: listă, tuplu, șir).
  • Elementul țintă pe care îl căutăm.

Se întoarce index a elementului pe care îl căutați dacă este găsit. Dacă elementul nu este găsit, se returnează -1.

Eficienţă

Este foarte eficient în comparație cu Căutarea liniară (căutarea unui element unul câte unul, începând cu primul), deoarece suntem capabili să „aruncăm” jumătate din listă la fiecare pas.

Să începem să ne scufundăm în acest algoritm.

🔸 Procedură vizuală

Vom aplica algoritmul Binary Search la această listă:

Cautare binara in Python o introducere vizuala

💡 Sfat: Observați că lista este deja sortată. Acesta a inclus indicii ca referință vizuală.

Poartă

Vrem să găsim indicele întregului 67.

Interval

Să ne prefacem că suntem algoritmul. Cum începem procesul?

Începem prin selectarea celor două limite ale intervalului în care dorim să căutăm. Vrem să căutăm întreaga listă, așa că selectăm indexul 0 ca limita inferioară și index 5 ca limita superioară:

1611624429 288 Cautare binara in Python o introducere vizuala

Element de mijloc

Acum trebuie să găsim indicele elementului de mijloc în acest interval. Facem acest lucru adăugând limita inferioară și limita superioară și împărțind rezultatul la 2 folosind împărțirea întreagă.

În acest caz, (0 + 5)//2 este 2 deoarece rezultatul 5/2 este 2.5 iar diviziunea întreagă trunchiază partea zecimală.

Deci elementul de mijloc este situat la indicele 2, iar elementul de mijloc este numărul 6:

1611624430 263 Cautare binara in Python o introducere vizuala

Comparații

Acum trebuie să începem să comparăm elementul de mijloc cu elementul nostru țintă pentru a vedea ce trebuie să facem în continuare.

Noi intrebam:
Este elementul de mijloc egal cu elementul pe care îl căutăm?

6 == 67 # False

Nu, nu este.

Așa că întrebăm:
Este elementul de mijloc mai mare decât elementul pe care îl căutăm?

6 > 67 # False

Nu, nu este.

Asa de elementul de mijloc este mai mic decât elementul pe care îl căutăm.

6 < 67 # True

Eliminați elementele

Întrucât lista este deja sortată, acest lucru ne spune ceva extrem de important. Ne spune că putem „arunca” jumătatea inferioară a listei, deoarece știm că toate elementele care vin înainte de elementul de mijloc vor fi mai mici decât elementul pe care îl căutăm, astfel încât elementul nostru țintă nu este acolo.

1611624430 420 Cautare binara in Python o introducere vizuala

Începeți din nou – Alegeți limitele

Ce facem în continuare? Am eliminat elementele și ciclul se repetă din nou.

Trebuie să alegem limitele pentru noul interval (a se vedea mai jos). Dar observați că limita superioară este păstrată intactă și numai limita inferioară este modificată.

1611624431 445 Cautare binara in Python o introducere vizuala

Acest lucru se datorează faptului că elementul pe care îl căutăm ar putea fi în jumătatea superioară a listei. Limita superioară este păstrată intactă și limita inferioară este schimbată pentru a „micșora” intervalul la un interval în care ar putea fi găsit elementul nostru țintă.

💡 Bacsis: Dacă elementul din mijloc ar fi fost mai mare decât elementul pe care îl căutăm, limita superioară ar fi fost schimbată și limita inferioară ar fi fost păstrată intactă. În acest fel, am renunța la jumătatea superioară a listei și am continua să căutăm în jumătatea inferioară.

Element de mijloc

Acum trebuie să găsim indicele elementului de mijloc adăugând limita inferioară la limita superioară și împărțind rezultatul la 2 folosind diviziunea întreagă.

Rezultatul (3+5)//2 este 4, deci elementul de mijloc este situat la index 4 iar elementul de mijloc este 67.

1611624431 934 Cautare binara in Python o introducere vizuala

Comparații

Noi intrebam:
Este elementul de mijloc egal cu elementul pe care îl căutăm?

67 == 67 # True

Da, este! Deci, am găsit elementul la index 4. Se returnează valoarea 4 și algoritmul a fost finalizat cu succes.

💡 Bacsis: Dacă elementul nu ar fi fost găsit, procesul ar fi continuat până când intervalul nu va mai fi valid. Dacă elementul nu ar fi fost găsit în întreaga listă, -1 ar fi fost returnat.

🔹 Cod pasaj

Acum, că aveți o intuiție vizuală a modului în care algoritmul funcționează în culise, să ne scufundăm în implementarea iterativă Python analizând-o linie cu linie:

def binary_search(data, elem):

    low = 0
    high = len(data) - 1

    while low <= high:
      
        middle = (low + high)//2
       
        if data[middle] == elem:
            return middle
        elif data[middle] > elem:
            high = middle - 1
        else:
            low = middle + 1

    return -1

Aici avem antetul funcției:

def binary_search(data, elem):

Este nevoie de două argumente:

  • Secvența ordonată a elementelor (de exemplu: listă, tuplu sau șir).
  • Elementul pe care vrem să-l găsim.

Interval inițial

Următoarea linie stabilește limitele inferioare și superioare inițiale:

low = 0
high = len(data) - 1

Limita inferioară inițială este index 0 iar limita superioară inițială este ultimul index al secvenței.

Buclă

Vom repeta procesul în timp ce există un interval valid, în timp ce limita inferioară este mai mică sau egală cu limita superioară.

while low <= high:

💡 Bacsis: Amintiți-vă că limitele sunt indicii.

Element de mijloc

La fiecare iterație, trebuie să găsim indexul elementului de mijloc. Pentru a face acest lucru, adăugăm limitele inferioară și superioară și împărțim rezultatul la 2 folosind divizarea întreagă.

middle = (low + high)//2

💡 Bacsis: Folosim divizarea numărului întreg în cazul în care lista sau intervalul conțin un număr par de elemente. De exemplu, dacă lista avea 6 elemente și nu am folosit diviziuni întregi, middle ar fi rezultatul (0 + 5)/2 care este 2.5. Un index nu poate fi un float, așa că tăiem porțiunea zecimală folosind // și selectați elementul la index 2.

Comparații

Cu aceste condiționale (a se vedea mai jos), determinăm ce să facem în funcție de valoarea elementului de mijloc data[middle]. O comparăm cu elementul țintă pe care îl căutăm.

if data[middle] == elem:
    return middle
elif data[middle] > elem:
    high = middle - 1
else:
    low = middle + 1

Există trei opțiuni:

  • Dacă elementul de mijloc este egal cu elementul pe care îl căutăm, returnăm indexul imediat pentru că am găsit elementul.
if data[middle] == elem:
    return middle
  • Dacă elementul din mijloc este mai mare decât elementul pe care îl căutăm, reasignăm limita superioară pentru că știm că elementul țintă se află în jumătatea inferioară a listei.
elif data[middle] > elem:
    high = middle - 1
  • Altfel, singura opțiune rămasă este că elementul din mijloc este mai mic decât elementul pe care îl căutăm, așa că redistribuim limita inferioară, deoarece știm că elementul țintă se află în jumătatea superioară a listei.
else:
    low = middle + 1

Elementul nu a fost găsit

Dacă bucla este finalizată fără a găsi elementul, se returnează valoarea -1.

return -1

și avem implementarea finală a algoritmului de căutare binară:

def binary_search(data, elem):

    low = 0
    high = len(data) - 1

    while low <= high:
      
        middle = (low + high)//2
       
        if data[middle] == elem:
            return middle
        elif data[middle] > elem:
            high = middle - 1
        else:
            low = middle + 1

    return -1

🔸 Cazuri speciale

Acestea sunt câteva cazuri particulare pe care le puteți găsi atunci când începeți să lucrați cu acest algoritm:

Elemente repetate

Dacă elementul pe care îl căutați se repetă în secvență, indexul returnat va depinde de numărul de elemente și de secvența de operații pe care algoritmul le efectuează asupra secvenței.

>>> >>> b = [2, 2, 3, 6, 7, 7]
>>> binary_search(b, 7)
4

Elementul nu a fost găsit

Dacă elementul nu este găsit, se returnează -1.

>>> b = [2, 2, 3, 6, 7, 7]
>>> binary_search(b, 8)
-1

Secvență goală

Dacă secvența este goală, -1 va fi returnat.

>>> b = []
>>> binary_search(b, 8)
-1

Secvență nesortată

Dacă secvența este nesortată, răspunsul nu va fi corect. Obținerea indexului corect este pură coincidență și s-ar putea datora ordinii elementelor din secvență și succesiunii operațiilor efectuate de algoritm.

Acest exemplu returnează rezultatul corect:

>>> b = [5, 7, 3, 0, -9, 2, 6]
>>> binary_search(b, 6)
6

Dar acesta nu:

>>> b = [5, 7, 3, 0, -9, 2, 10, 6]
>>> binary_search(b, 6)
-1

💡 Bacsis: Gândiți-vă de ce primul exemplu returnează rezultatul corect. Sugestie: este pură coincidență faptul că ordinea elementelor se întâmplă pentru a face algoritmul să atingă indicele corect, dar procesul pas cu pas evaluează 0, atunci 2, și, în sfârșit 6. În acest caz particular, pentru acest element particular, indexul corect este găsit chiar dacă secvența nu este sortată.

🔹 Un exemplu mai complex

Acum, că sunteți mai familiarizați cu algoritmul și implementarea lui Python, aici avem un exemplu mai complex:

Vrem să găsim indexul elementului 45 în această listă folosind Căutare binară:

1611624431 238 Cautare binara in Python o introducere vizuala

Prima iterație

Limitele inferioară și superioară sunt selectate:

1611624432 822 Cautare binara in Python o introducere vizuala

Elementul de mijloc (26) este selectat:

1611624432 838 Cautare binara in Python o introducere vizuala

Dar elementul de mijloc (26) nu este elementul pe care îl căutăm, este mai mic decât 45:

1611624433 77 Cautare binara in Python o introducere vizuala

A doua iterație

Deci, putem arunca toate elementele care sunt mai mici decât elementul din mijloc și putem selecta limite noi. Noua limită inferioară (27) este elementul situat imediat în dreapta elementului de mijloc anterior:

1611624433 293 Cautare binara in Python o introducere vizuala

💡 Bacsis: Amintiți-vă că lista este deja sortată.

Noul element de mijloc (30) este selectat:

1611624433 892 Cautare binara in Python o introducere vizuala

Elementul de mijloc (30) nu este elementul pe care îl căutăm, este mai mic decât 45:

1611624434 813 Cautare binara in Python o introducere vizuala

A treia iterație

Putem arunca elementele care sunt mai mici sau egale cu 30 care nu au fost deja aruncate. Limita inferioară este actualizată la 32:

1611624434 184 Cautare binara in Python o introducere vizuala

Aici avem un caz interesant: elementul de mijloc este una dintre limitele intervalului curent deoarece (7+8)//2 este 7.

1611624435 731 Cautare binara in Python o introducere vizuala

Elementul de mijloc (32) nu este elementul pe care îl căutăm (45), este mai mic.

1611624435 656 Cautare binara in Python o introducere vizuala

A patra iterație

Putem arunca elementele care sunt mai mici sau egale cu 32 care nu au fost deja aruncate.

Aici avem un alt caz foarte interesant: intervalul are doar un element.

1611624436 932 Cautare binara in Python o introducere vizuala

💡 Bacsis: Acest interval este valid deoarece am scris această condiție while high <= low: , care include intervale în care indicele limitei inferioare este egal cu indicele limitei superioare.

Elementul de mijloc este singurul element din interval deoarece (8+8)//2 este 8, deci indicele elementului de mijloc este 8 iar elementul de mijloc este 45.

1611624436 295 Cautare binara in Python o introducere vizuala

Acum elementul de mijloc este elementul pe care îl căutăm, 45:

1611624436 747 Cautare binara in Python o introducere vizuala

Deci valoarea 8 (indexul) este returnat:

>>> binary_search([1, 3, 7, 15, 26, 27, 30, 32, 45], 45)
8

🔸 Practică suplimentară

Dacă doriți să aveți o practică suplimentară cu acest algoritm, încercați să explicați cum funcționează algoritmul din culise atunci când este aplicat acestei liste pentru a găsi numărul întreg 90:

[5, 8, 15, 26, 38, 56]
  • Ce se întâmplă pas cu pas?
  • Ce valoare este returnată?
  • Se găsește elementul?

Sper cu adevărat că ți-a plăcut articolul meu și l-ai găsit util. Acum puteți implementa algoritmul de căutare binară în Python. Vezi cursul meu online “Algoritmi de căutare și sortare Python: o abordare practică“. Urmează-mă Stare de nervozitate. ⭐️