Treci la conținutul principal

De ce măsurăm algoritmii

Același program rulează repede pe un calculator nou și încet pe unul vechi. Așadar secundele sunt un mod slab de a compara doi algoritmi — numărul depinde de mașină, nu doar de cod.

În schimb numărăm pași: câte operații simple face algoritmul. Un pas este o acțiune mică, precum citirea unei valori, compararea a două numere sau adunarea lor.

Notația Big O descrie cum crește numărul de pași atunci când crește intrarea. Ea nu spune „asta durează 3 secunde”. Ea spune „dacă intrarea se dublează, munca se dublează” — adică O(n) — sau „dacă intrarea se dublează, munca rămâne aceeași” — adică O(1).

Big O se uită la cazul cel mai defavorabil: cei mai mulți pași de care poate avea nevoie vreodată algoritmul pentru o intrare de dimensiune n.


Numărarea pașilor

O buclă care vizitează fiecare element al unui tablou face o trecere pentru fiecare element. Pentru 5 elemente înseamnă 5 pași, pentru 10 elemente 10 pași. Numărul de pași este același cu nums.length.

Nu ne interesează numărul exact. Ne interesează forma creșterii: intrare dublă, pași dubli. Această formă se numește liniară și se scrie O(n).

Numără pașii unei bucle

Funcția adaugă un contor steps++ în interiorul buclei. Un tablou de 5 elemente cere 5 pași, un tablou de 10 elemente cere 10 pași — numărul urmează dimensiunea intrării.

Prompt
Scrie o funcție TypeScript care adună un tablou de numere și numără câți pași de buclă face, folosind un contor steps. Apeleaz-o o dată cu un tablou de 5 numere și o dată cu un tablou de 10 numere și afișează ambele numere de pași.
Ieșire: program TypeScript și rezultatul capturat
Count the steps of a loop

Dublarea intrării dublează munca

Programul rulează aceeași buclă de numărare pentru n = 100, 200 și 400. De fiecare dată când n se dublează, se dublează și numărul de pași. Aceasta este amprenta lui O(n).

Prompt
Scrie un program TypeScript care, pentru n = 100, 200 și 400, rulează o buclă de n ori numărând pașii și afișează câte o linie pentru fiecare n, cu n și numărul de pași, ca să pot vedea că dublarea lui n dublează pașii. Aliniază numerele ca să se potrivească coloanele.
Ieșire: program TypeScript și rezultatul capturat
Doubling the input doubles the work

Clasele de creștere

Majoritatea algoritmilor se încadrează în câteva clase de creștere obișnuite, de la cea mai rapidă la cea mai lentă:

O(1) constantă — munca nu crește niciodată.
O(log n) logaritmică — dublarea intrării adaugă un singur pas.
O(n) liniară — munca crește odată cu intrarea.
O(n log n) — un algoritm bun de sortare.
O(n²) pătratică — o buclă în interiorul altei bucle.
O(2ⁿ) exponențială — munca se dublează cu fiecare element în plus.

Pentru intrări mici diferența este minusculă. Pentru intrări mari ea decide dacă programul răspunde imediat sau nu se termină niciodată.

Compară clasele de creștere

Tabelul arată numărul de pași pentru un n care crește. La n = 16, log n este doar 4, în timp ce 2ⁿ este deja 65536. Cu cât n devine mai mare, cu atât clasele se îndepărtează mai mult una de alta.

Prompt
Scrie un program TypeScript care afișează un tabel ce compară clasele de creștere. Pentru n = 1, 2, 4, 8, 16 afișează câte un rând cu: n, log n, n, n log n, n la pătrat și 2 la puterea n. Folosește Math.log2 și aliniază coloanele cu padStart.
Ieșire: program TypeScript și rezultatul capturat
Compare the growth classes

Ce ignoră Big O

Big O păstrează doar forma creșterii și aruncă detaliile. O(2n), O(n + 5) și O(n) fac toate parte din aceeași clasă: liniară.

De ce? Pentru că la intrări mari factorul constant încetează să conteze. Un algoritm care face 2 treceri peste un milion de elemente este oricum într-o cu totul altă lume decât unul care compară fiecare element cu fiecare alt element.

Regulile exacte de simplificare — renunță la constante, renunță la termenii mai mici — au propria lor pagină mai târziu în acest grup.

Două bucle înseamnă tot O(n)

Programul face două parcurgeri separate ale tabloului: una pentru sumă, una pentru cea mai mare valoare. Asta înseamnă 2n pași — 16 pentru 8 elemente. Big O renunță la constanta 2, deci acest lucru înseamnă tot O(n).

Prompt
Scrie un program TypeScript care face două parcurgeri separate ale aceluiași tablou de 8 numere — o buclă pentru a calcula suma, o buclă pentru a găsi cea mai mare valoare — numărând fiecare pas de buclă într-un singur contor comun. Afișează suma, cea mai mare valoare, numărul de elemente și totalul pașilor.
Ieșire: program TypeScript și rezultatul capturat
Two loops are still O(n)