I matematik og datalogi er Big O-notationen en måde at sammenligne vækstrater for forskellige funktioner på. Det bruges ofte til at sammenligne effektiviteten af forskellige algoritmer, hvilket gøres ved at beregne, hvor meget hukommelse der er brug for, og hvor lang tid det tager at gennemføre.
Big O-notationen bruges ofte til at identificere, hvor komplekst et problem er, også kendt som problemets kompleksitetsklasse. Matematikeren Paul Bachmann (1837-1920) var den første til at bruge denne notation i anden udgave af sin bog "Analytische Zahlentheorie" i 1896. Edmund Landau (1877-1938) gjorde notationen populær. Når man taler om Landau-symboler, henviser man derfor til denne notation.
Big O-notationen er opkaldt efter udtrykket "funktionens orden", som henviser til funktionernes vækst. Big O notation bruges til at finde den øvre grænse (det højest mulige beløb) for funktionens vækstrate, hvilket betyder, at den beregner den længste tid, det vil tage at omdanne input til output. Det betyder, at en algoritme kan grupperes efter, hvor lang tid den kan tage i et worst-case-scenarie, hvor den længste vej vil blive taget hver gang.
Mere specifikt, givet to positive funktioner og
, siger vi, at
er i det store O af
(skrevet
), hvis for stort nok
,
for en vis konstant
.
Big O er et udtryk, der finder den værst tænkelige kørselstid og viser, hvor effektiv en algoritme er uden at skulle køre programmet på en computer. Dette er også nyttigt på grund af det faktum, at forskellige computere kan have forskellig hardware og derfor har brug for forskellig tid til at gennemføre det. Da Big O altid tager udgangspunkt i det værst tænkelige tilfælde, kan den vise en konsekvent måling af hastigheden: Uanset hardwaren vil altid være hurtigere færdig end
, fordi de har forskellige grader af effektivitet.
Grundlæggende principper
- Øvre grænse: Big O angiver en øvre grænse for, hvordan en funktion (typisk en algoritmes kørselstid eller pladsforbrug) vokser, når inputstørrelsen n bliver stor.
- Abstraktion fra konstantfaktorer: Konstante faktorer og lavere ordens termer ignoreres — for eksempel er 3n + 5 i praksis O(n).
- Værstetilfælde: Big O beskriver ofte værstetilfældet, men kan også bruges til at udtrykke asymptotisk vækst uden at specificere kontekst (tid, plads, bedste/gennemsnit).
Almindelige kompleksiteter og eksempler
- O(1) — konstant tid. Eksempel: adgang til et array-element ved indeks.
- O(log n) — logaritmisk tid. Eksempel: binær søgning i et sorteret array.
- O(n) — lineær tid. Eksempel: gennemløb af en liste for at finde et element.
- O(n log n) — typisk for effektive sorteringsalgoritmer som mergesort eller heapsort.
- O(n^2) — kvadratisk tid. Eksempel: dobbelt nestede loops som i simple sorteringsalgoritmer (bubble sort, insertion sort i værste tilfælde).
- O(2^n) — eksponentiel tid. Eksempel: brute-force løsning af nogle komplekse kombinatoriske problemer.
- O(n!) — faktorial tid. Eksempel: permuteringsproblemer, som genererer alle mulige rækkefølger.
Regneregler og tommelfingerregler
- Drop konstante faktorer: a·f(n) er i samme klasse som f(n) for konstant a > 0.
- Drop lavere ordens termer: f(n) + g(n) er i samme klasse som max(f(n), g(n)).
- Produkter og sammensætninger: Hvis en algoritme indeholder et loop i et loop, får man ofte n·m eller n^2 afhængigt af grænserne.
Værste-, bedste- og gennemsnitstilfælde
Big O bruges ofte om værstetilfælde, men man kan også beskrive bedste- og gennemsnitstilfælde:
- Worst case (O): Maksimal tid/ressourceforbrug over alle input af størrelse n.
- Best case (Ω): Minimal tid/ressourceforbrug (Landau Ω-notation).
- Gennemsnit (Θ eller forventet): Typisk middelværdi over alle input (Landau Θ-notation bruges til at angive både øvre og nedre grænse, når de er ens asymptotisk).
Analyseteknikker — hvordan man finder Big O
- Lineære loops: Et enkelt loop fra 1 til n giver O(n).
- Nested loops: To fuldt indlejrede loops over n giver O(n^2).
- Konsekutive blokke: Hvis du har to blokke K1 og K2 kørt efter hinanden med kompleksiteter f(n) og g(n), er totalen O(max(f(n), g(n))).
- Rekursion: Anvend rekurrence-relationer (f.eks. Master-teoremet) til at bestemme kompleksiteten.
- Datastruktureffekter: Operationer på forskellige datastrukturer har forskellig kompleksitet (indsættelse i en hash-tabel vs. sorteret træ osv.).
Tid vs. plads
Big O bruges både om tid (hvor mange operationer) og plads (hukommelsesforbrug). Nogle algoritmer bytter tid for plads og omvendt (f.eks. memoization i dynamisk programmering bruger ekstra plads for at spare tid).
Praktiske overvejelser og begrænsninger
- Big O beskriver asymptotisk opførsel for store n. For små inputs kan konstante faktorer og implementeringsdetaljer dominere.
- Det angiver ikke nødvendigvis hvor hurtig en algoritme er i praksis på en given maskine — men det giver værdifuld sammenligning ved storskala input.
- Gode optimeringer kan ændre konstante faktorer; nogle gange er en algoritme med dårligere asymptotik bedre for realistiske inputstørrelser.
Praktiske tips
- Når du vurderer løsninger, start med asymptotisk analyse for at udelukke umulige strategier (fx O(n!) for store n).
- Brug profilering og målinger til at finjustere implementeringer og vælge datastrukturer.
- Lær at genkende mønstre (divide-and-conquer, dynamisk programmering, greedy), da de ofte fører til typiske kompleksiteter.
Big O-notation er et kraftfuldt værktøj til at forstå og sammenligne algoritmer. Den kombinerer en enkel, matematisk definition med praktisk relevans, så man kan forudsige skalerbarhed og træffe informerede valg ved design og implementering af programmer.