Treci la conținutul principal

Împarte problema în două

O(log n) înseamnă timp logaritmic: fiecare pas reduce la jumătate munca rămasă. Pentru că înjumătățirea micșorează numerele atât de repede, dublarea intrării adaugă doar un pas în plus.

16 elemente cer 4 înjumătățiri, 32 de elemente cer 5, un milion aproximativ 20, un miliard aproximativ 30. De aceea algoritmii logaritmici par instantanei chiar și pe date uriașe.

Exemplul clasic este căutarea binară — dar ea funcționează numai pe date sortate. Înjumătățirea are nevoie de o ordine ca să știe care jumătate poate fi aruncată.


O buclă de înjumătățire

Cel mai simplu mod de a vedea O(log n) este o buclă care împarte n la 2 până ajunge la 1. Numărul de runde este logaritmul lui n: pentru n = 16 bucla rulează de 4 ori, pentru că 2 · 2 · 2 · 2 = 16.

Înjumătățire până rămâne unu

16 devine 8, 4, 2, 1 — patru pași. Dublarea lui n la 32 ar adăuga doar un singur pas în plus.

Prompt
Scrie un program TypeScript care pornește de la n = 16 și împarte n la 2 într-o buclă până ajunge la 1, afișând n după fiecare împărțire și numărând pașii. La final afișează câți pași au fost necesari.
Ieșire: program TypeScript și rezultatul capturat
Halving until one remains

Căutarea binară

Căutarea binară găsește o valoare într-un tablou sortat verificând mereu elementul din mijloc. Dacă mijlocul este prea mic, toată jumătatea din stânga este aruncată; dacă este prea mare, jumătatea din dreapta. Fiecare verificare înjumătățește intervalul de căutare.

Două marcaje, lo și hi, țin intervalul curent. Bucla se termină când valoarea este găsită sau când intervalul este gol.

TypeScript nu are o căutare binară proprie. Căutările încorporate — indexOf() și includes() — parcurg tabloul de la început, deci rămân O(n) chiar și atunci când datele sunt deja sortate. Pe date sortate, bucla scrisă de mână este cea mai rapidă.

Căutarea binară pas cu pas

Într-un tablou sortat de 16 numere, căutarea lui 13 are nevoie de doar 4 verificări: intervalul se micșorează 16 → 8 → 4 → 2 și se oprește la indexul 6. O căutare liniară ar fi avut nevoie aici de 7 pași — și de până la 16.

Prompt
Scrie un program TypeScript care caută binar în tabloul sortat 1, 3, 5, 7, ... 31 (numerele impare, 16 elemente) valoarea 13, folosind marcajele lo și hi și numărând verificările. Când o găsește, afișează valoarea, indexul ei și numărul de verificări.
Ieșire: program TypeScript și rezultatul capturat
Binary search step by step

Căutarea încorporată este tot liniară

Ambele căutări sunt de acord: 9 se află la indexul 4. Dar indexOf() ajunge acolo testând 5 elemente, în timp ce căutarea binară are nevoie de 3 verificări. Pentru valoarea lipsă 10, indexOf() testează toate cele 8 elemente și returnează -1; căutarea binară se oprește după 3 verificări și poate chiar să raporteze indexul 5 — locul unde ar încăpea 10 ca tabloul să rămână sortat.

Cu 8 elemente diferența este mică. Cu un milion sunt aproximativ 1.000.000 de pași față de 20.

Prompt
Scrie un program TypeScript care compară căutarea încorporată cu o căutare binară scrisă de mână pe tabloul sortat 1, 3, 5, 7, 9, 11, 13, 15. Pentru ținta 9 și pentru ținta lipsă 10, afișează ce returnează indexOf și includes plus câte elemente a atins parcurgerea și afișează rezultatul căutării binare cu numărul ei de verificări și, atunci când valoarea lipsește, indexul unde ar încăpea.
Ieșire: program TypeScript și rezultatul capturat
The built-in lookup is still linear

O(n log n): sortarea

O(n log n) se află între liniar și pătratic și este complexitatea algoritmilor buni de sortare: datele sunt înjumătățite din nou și din nou (log n niveluri), iar fiecare nivel face o cantitate liniară de muncă (n).

Rareori scrii singur o sortare — fiecare tablou are deja sort(), iar acesta este O(n log n). Sortarea o dată, apoi căutarea binară de multe ori, este un tipar obișnuit și rapid.

Sortarea unui tablou

nums.sort((a, b) => a - b) ordonează cele 8 numere în aproximativ 8 · log 8 = 24 pași — mult mai puțini decât cei 64 pe care i-ar folosi o sortare pătratică, iar diferența crește repede pe măsură ce tabloul se mărește.

Comparatorul nu este opțional aici. Apelat fără el, sort() compară valorile ca text: "13" începe cu 1, deci ajunge înaintea lui "5". Programul afișează ambele ordini.

Prompt
Scrie un program TypeScript care sortează o copie a tabloului 31, 5, 17, 1, 23, 9, 27, 13 de două ori: o dată cu un apel simplu sort() și o dată cu comparatorul (a, b) => a - b. Afișează ambele rezultate, ca să fie vizibilă ordinea ca text a apelului simplu.
Ieșire: program TypeScript și rezultatul capturat
Sort an array