TDT4120 Algoritmer og datastrukturer Interaktivt kompendium · H2026
Fremgang
0/15

01

Algoritmer og kompleksitet

Kjernepensum

To metoder kan løse samme problem og likevel ikke være i samme klasse. Skal du pare givere og mottakere i et donasjonsregister, kan du prøve alle koblinger etter tur eller kjøre Ford-Fulkerson. Med nn personer på hver side har opptellingen n!n! koblinger å gå gjennom, mens Ford-Fulkerson gjør i størrelsesorden n3n^3 operasjoner. Med fire personer står det 2424 mot 6464, og opptellingen ser billigst ut. Dobler du til åtte, står det 40 32040\,320 mot 512512.

Problem, instans og maskin

Et problem er en relasjon mellom input og output. Sortering krever at outputen er en stigende omordning av inputen, og sier ingenting om hvilke tall som kommer inn. En instans er én bestemt input, for eksempel ⟨6,8,3,5⟩\langle 6, 8, 3, 5 \rangle. Problemstørrelsen nn er plassen en instans trenger, som regel antall elementer i den.

Kjøretiden er en funksjon av nn. For at den funksjonen skal si noe som gjelder på andre maskiner enn din egen, regner du i RAM-modellen: aritmetikk, sammenligning, hopp og oppslag i én celle koster én tidsenhet hver, og instruksjonene utføres etter hverandre. Heltallene har begrenset bredde, om lag clg⁡nc \lg n bits, akkurat nok til å indeksere hele inputen. Du kan regne med større tall enn det, slik kryptografi gjør, men da holder ikke antagelsen om konstant tid per operasjon lenger.

Vekstrater

Anta ett mikrosekund per operasjon og hundre år til rådighet. Da rekker du disse problemstørrelsene:

Alt på formen nkn^k er polynomisk.
KjøretidNavnStørste nn på hundre år
lg⁡n\lg nlogaritmisk23,2⋅10152^{3{,}2 \cdot 10^{15}}
nnlineær3,2⋅10153{,}2 \cdot 10^{15}
nlg⁡nn \lg nlinearitmisk6,9⋅10136{,}9 \cdot 10^{13}
n2n^2kvadratisk56 000 00056\,000\,000
n3n^3kubisk146 000146\,000
2n2^neksponentiell5151
n!n!faktoriell1717
Logaritmisk plott av antall operasjoner mot problemstørrelse for n, n lg n, n i andre, n i tredje, to opphøyd i n og n fakultet. De to siste stiger langt raskere enn de fire første.
Figur 1. Antall operasjoner mot nn. Y-aksen er logaritmisk, så avstanden mellom to kurver er faktoren mellom dem.

En raskere maskin hjelper mindre enn en bedre algoritme. Kjøper du en maskin som er tusen ganger raskere, flytter du de 17 faktorielle elementene til 20. Bytter du til en kvadratisk algoritme, flytter du dem til titalls millioner.

Asymptotisk notasjon

For å sammenligne vekst uten å telle enkeltoperasjoner må du se bort fra konstantfaktorer og lavere ordens ledd. Konstantfaktorene forsvinner hvis gg får skaleres med en konstant cc før sammenligningen. De lavere ordens leddene forsvinner hvis sammenligningen bare trenger å holde fra en n0n_0 og oppover, der det ledende leddet har tatt over. De to kravene gir definisjonen:

Ta f(n)=n2+100nf(n) = n^2 + 100n og g(n)=n2g(n) = n^2. Den nedre grensen holder med c1=1c_1 = 1 for alle nn. Den øvre holder med c2=2c_2 = 2 når 100n≤n2100n \le n^2, altså fra n0=100n_0 = 100. Til venstre for n0n_0 ligger ff over 2n22n^2, og det er tillatt, for definisjonen krever ulikheten bare fra n0n_0 og oppover. Altså er n2+100n=Θ(n2)n^2 + 100n = \Theta(n^2), og det lavere ordens leddet 100n100n er borte.

Tre kurver mot n. Fra n lik hundre ligger f mellom n i andre og to n i andre. Til venstre for hundre ligger f over begge.
Figur 2. f(n)=n2+100nf(n) = n^2 + 100n mellom c1g(n)=n2c_1 g(n) = n^2 og c2g(n)=2n2c_2 g(n) = 2n^2, med n0=100n_0 = 100 markert.

De to strenge operatorene

OO sier at ff vokser høyst så raskt som gg, og tillater at de to vokser like raskt. 2n2=O(n2)2n^2 = O(n^2) og 2n=O(n2)2n = O(n^2) er begge sanne, men bare den første er stram. For en øvre grense som ikke er stram bruker du oo, og der må f(n)<c g(n)f(n) < c\,g(n) holde for enhver c>0c > 0, ikke bare for én eneste. Det er en langt sterkere påstand, og den er ekvivalent med at f(n)/g(n)f(n)/g(n) går mot null. Så 2n=o(n2)2n = o(n^2), mens 2n2≠o(n2)2n^2 \ne o(n^2).

ω\omega er det samme speilvendt: en nedre grense som ikke er stram, med c g(n)<f(n)c\,g(n) < f(n) for enhver cc. Da vokser f(n)/g(n)f(n)/g(n) over alle grenser, og f(n)=o(g(n))f(n) = o(g(n)) er nøyaktig det samme som g(n)=ω(f(n))g(n) = \omega(f(n)).

Alle fem oppfører seg som sammenligning av to tall, og det er den raskeste veien til å huske dem:

De fem operatorene som sammenligning av to tall aa og bb, etter CLRS §3.2.
UtsagnLeses somGrensen f(n)/g(n)f(n)/g(n)Som tall
f=O(g)f = O(g)vokser høyst så raskt som ggendeliga≤ba \le b
f=Ω(g)f = \Omega(g)vokser minst så raskt som ggstørre enn nulla≥ba \ge b
f=Θ(g)f = \Theta(g)vokser like raskt som ggen konstant, verken 00 eller ∞\inftya=ba = b
f=o(g)f = o(g)vokser strengt saktere enn gg00a<ba < b
f=ω(g)f = \omega(g)vokser strengt raskere enn gg∞\inftya>ba > b

Analogien holder for transitivitet og for det meste ellers, med ett unntak. To tall kan alltid sammenlignes, men to funksjoner kan mangle et forhold i det hele tatt: nn og n1+sin⁡nn^{1+\sin n} er verken OO eller Ω\Omega av hverandre, fordi forholdet nsin⁡nn^{\sin n} mellom dem svinger mellom omtrent 1/n1/n og nn.

Notasjonen dukker også opp midt inne i et uttrykk. Da står den for en ukjent funksjon av den oppgitte ordenen, slik at n+O(n)n + O(n) betyr «nn pluss en eller annen funksjon som er O(n)O(n)». Slike ligninger leses fra venstre mot høyre, og høyresiden må være minst like generell som venstresiden. Derfor stemmer n+O(n)=O(n)n + O(n) = O(n), mens O(n)=n+O(n)O(n) = n + O(n) er galt.

Regneeksempel Forenkle O(na)+Ω(nb)+Θ(nc)O(n^a) + \Omega(n^b) + \Theta(n^c)

Finn det enkleste ekvivalente uttrykket, uten å tape presisjon.

Vis løsning Skjul løsning

Ta de øvre og de nedre grensene hver for seg. O(na)O(n^a) gir en øvre grense nan^a og ingen nedre, så den nedre er 00. Ω(nb)\Omega(n^b) gir en nedre grense nbn^b og ingen øvre. Θ(nc)\Theta(n^c) gir begge, ncn^c.

Summen av de nedre grensene er 0+nb+nc0 + n^b + n^c. Summen av de øvre er ubegrenset, fordi Ω(nb)\Omega(n^b) ikke setter noen tak. Da står en nedre grense alene igjen, og en nedre grense alene skrives med Ω\Omega. Regelen står som én rad i Formelsamlingen.

Svar
Ω(nb+nc)\Omega(n^b + n^c)
Eksamensoppgave Velg én beskrivelse av kjøretiden
Ordinær eksamen, desember 2025, oppgave 2

Din venn Lurvik har klart å vise at to av følgende beskrivelser av kjøretiden til en algoritme er korrekte, men han husker ikke hvilke to:

  1. T(n)=O(n3)T(n) = O(n^3)
  2. T(n)=Ω(n3)T(n) = \Omega(n^3)
  3. T(n)=Θ(n3)T(n) = \Theta(n^3)

Sjefen hans har bedt ham velge ut én av beskrivelsene, og nå har Lurvik bedt deg om hjelp. Hvilken av de tre velger du (nummer 1, 2 eller 3)? Forklar kort hvorfor dette er et godt valg.

Vis løsning Skjul løsning

Θ\Theta betyr «både OO og Ω\Omega». Har Lurvik vist to av de tre, er enten nummer 3 en av dem, eller så har han vist både 1 og 2, og de to til sammen er nettopp nummer 3. Uansett stemmer T(n)=Θ(n3)T(n) = \Theta(n^3), og det er den mest informative beskrivelsen, siden den gir både en øvre og en nedre grense.

Svar
Nummer 3, T(n)=Θ(n3)T(n) = \Theta(n^3).
Eksamensoppgave Kjøretiden til Fung-Sort
Kontinuasjonseksamen, august 2023, oppgave 1

Algoritme 1 sorterer tabellen A[1:n]A[1 : n]. Hva er kjøretiden, som funksjon av nn? Oppgi svaret i asymptotisk notasjon.

Algoritme 1
Fung-Sort(A, n)
1 for i = 1 to n
2     for j = 1 to n
3         if A[i] < A[j]
4             swap A[i] and A[j]
Vis løsning Skjul løsning

Den ytre løkka kjører nn ganger, og for hver av dem kjører den indre løkka nn ganger. Kroppen er en sammenligning og eventuelt et bytte, begge konstant tid. Ingen av løkkene har en betingelse som kan stoppe dem tidlig, så antallet iterasjoner er n⋅nn \cdot n uansett hvordan inputen ser ut.

Svar
Θ(n2)\Theta(n^2)

Insertion-Sort

Insertion-Sort sorterer slik mange sorterer kort på hånden. Den venstre delen av tabellen er sortert. Hver iterasjon løfter ut det neste elementet, nøkkelen, skyver hvert større element i den sorterte delen ett hakk mot høyre og setter nøkkelen inn i den ledige plassen.

insertion_sort.py
def insertion_sort(A, n):  # indekser fra 0, læreboka teller fra 1
  for i in range(1, n):
      key = A[i]
      # Sett key inn i den sorterte delen A[0 : i]
      j = i - 1
      while j >= 0 and A[j] > key:
          A[j + 1] = A[j]
          j = j - 1
      A[j + 1] = key
  return A
Insertion-Sort
0 / 0

Grønt er den sorterte delen, og boksen over tabellen holder nøkkelen.

Løkkeinvarianter

Stepperen viser at én tabell blir sortert. For å vise at enhver tabell blir det, bruker du induksjon over iterasjonene, med en påstand om tilstanden ved starten av hver iterasjon. Når løkka er ferdig, skal hele AA være sortert og inneholde de elementene som lå der fra begynnelsen. En påstand som gir det til slutt, er at A[0..i−1]A[0 .. i - 1] ved starten av hver iterasjon består av de elementene som lå der fra begynnelsen, i sortert rekkefølge. Segmentet A[0..i−1]A[0 .. i - 1] har begge ender med; i Python skrives det A[0 : i].

  • Initialisering: før første iterasjon er i=1i = 1, og A[0..0]A[0 .. 0] har ett element, så det er sortert.
  • Vedlikehold: iterasjonen løfter ut nøkkelen A[i]A[i], skyver hvert større element i A[0..i−1]A[0 .. i - 1] ett hakk mot høyre og setter nøkkelen inn i den ledige plassen. Da er A[0..i]A[0 .. i] sortert, og det er påstanden for neste verdi av ii.
  • Terminering: løkka stopper etter iterasjonen med i=n−1i = n - 1, og da gir vedlikeholdet at A[0..n−1]A[0 .. n - 1], altså hele tabellen, er sortert.

En slik påstand kalles en løkkeinvariant, og de tre delene er det generelle mønsteret:

Initialisering er grunntilfellet og vedlikehold er induksjonssteget. Terminering er det løkker har i tillegg: beviset er verdiløst om løkka aldri stopper.

Beviset for Insertion-Sort er svak induksjon, for vedlikeholdet bruker bare påstanden fra forrige iterasjon. Sterk induksjon antar påstanden for alle mindre tilfeller. Den passer når en algoritme kaller seg selv på instanser som er mindre enn n−1n - 1, for eksempel halvparten: beviset for størrelse nn bruker da påstanden for n/2n/2, ikke for n−1n - 1.

Kjøretid

Den ytre løkka går n−1n-1 ganger uansett input. Den indre gjør et sted mellom 0 og ii forskyvninger, og det er der forskjellen mellom instansene ligger. Er tabellen sortert fra før, er A[j] aldri større enn nøkkelen, while-testen slår feil med én gang, og arbeidet er Θ(n)\Theta(n) totalt. I en tilfeldig rekkefølge er nøkkelen i snitt mindre enn halvparten av elementene foran seg, så iterasjon ii gjør i snitt i/2i/2 forskyvninger, til sammen omtrent n2/4n^2/4, og det er Θ(n2)\Theta(n^2). Insertion-Sort bruker O(1)O(1) ekstra minne, og like elementer beholder rekkefølgen sin.

Regneeksempel Tabell sortert synkende

Tabellen er sortert synkende, ⟨n,n−1,…,1⟩\langle n, n-1, \ldots, 1 \rangle. Hvor mange forskyvninger gjør algoritmen?

Vis løsning Skjul løsning

I iterasjon ii er A[i] mindre enn hvert av de ii elementene foran seg, så nøkkelen må forbi dem alle. Det er ii forskyvninger. Lagt sammen over iterasjonene i=1,…,n−1i = 1, \ldots, n-1:

∑i=1n−1i  =  n(n−1)2\sum_{i=1}^{n-1} i \;=\; \frac{n(n-1)}{2}

Summen er trekanttallet, et halvt rektangel med sider nn og n−1n-1. Uten konstantfaktoren og det lineære leddet står n2n^2 igjen.

Svar
n(n−1)/2n(n-1)/2 forskyvninger, altså Θ(n2)\Theta(n^2)

Grenser og tilfeller

Insertion-Sort bruker Θ(n)\Theta(n) på en tabell som er sortert fra før og Θ(n2)\Theta(n^2) på en som er sortert synkende, så størrelsen alene bestemmer ikke kjøretiden. For hver nn finnes det mange instanser, og de koster ulikt. Beste tilfelle er den billigste instansen av størrelse nn, verste tilfelle den dyreste, og gjennomsnittstilfellet er den forventede kostnaden når instansen trekkes tilfeldig, for sortering med alle rekkefølger like sannsynlige. Hvert av de tre er en egen funksjon av nn.

Beste, verste og gjennomsnittlige tilfelle

Hvert lyse punkt er én tilfeldig instans av størrelse nn med kostnaden Insertion-Sort fikk på den, og de mørke punktene er den billigste og den dyreste. De fargede kurvene er beste, gjennomsnittlige og verste tilfelle, og de stiplede kurvene er den valgte grensen.

Hvilket tilfelle du snakker om, og hvor stramt du begrenser det, er to uavhengige valg. Hver av OO, Ω\Omega og Θ\Theta kan brukes på hvert av de tre tilfellene, og oo og ω\omega på samme måte. For Insertion-Sort gir det disse grensene:

Insertion-Sort, stramme grenser i hver rute. Vet du ikke hvilken instans du får, må den øvre grensen dekke det verste tilfellet og den nedre det beste, og da finnes det ingen Θ\Theta.
TilfelleOOΩ\OmegaΘ\Theta
BesteO(n)O(n)Ω(n)\Omega(n)Θ(n)\Theta(n)
GjennomsnittligeO(n2)O(n^2)Ω(n2)\Omega(n^2)Θ(n2)\Theta(n^2)
VersteO(n2)O(n^2)Ω(n2)\Omega(n^2)Θ(n2)\Theta(n^2)
Ukjent inputO(n2)O(n^2)Ω(n)\Omega(n)ingen

Utelater du tilfellet, gjelder påstanden alle instanser. «Insertion-Sort bruker O(n2)O(n^2)» er da riktig, og «Ω(n)\Omega(n)» også, mens «Θ(n2)\Theta(n^2)» er galt: på en sortert tabell bruker den Θ(n)\Theta(n).

Eksamensoppgave Gjennomsnittet kan være lik ett av ytterpunktene
Ordinær eksamen, desember 2025, oppgave 4

Lurvik og Smartnes har en heftig diskusjon. Smartnes mener man kan ha en algoritme med ulik asymptotisk kjøretid i beste og verste tilfelle, der den gjennomsnittlige kjøretiden likevel er lik én av de to. Lurvik mener dette er umulig, siden gjennomsnittet yy av xx og zz alltid vil ligge imellom dem, altså x<y<zx < y < z.

Bruk et eksempel fra pensum for å vise at Smartnes har rett. Forklar kort hvorfor det er mulig.

Smartnes mener altså at average-case kan være lik enten best-case eller worst-case, selv om best-case og worst-case er forskjellige.

Vis løsning Skjul løsning

Insertion-Sort er eksempelet: beste tilfelle er Θ(n)\Theta(n), verste tilfelle er Θ(n2)\Theta(n^2), og gjennomsnittet er også Θ(n2)\Theta(n^2). Lurviks innvending holder for tall, men ikke for vekstrater. Målt i antall operasjoner ligger gjennomsnittet for Insertion-Sort omtrent på halvparten av verste tilfelle, og den faktoren forsvinner i notasjonen. Θ\Theta skjuler konstantfaktorer, så to kjøretider som er ulike som tall kan ha samme asymptotiske vekst.

Svar
Insertion-Sort: Θ(n)\Theta(n) i beste tilfelle, Θ(n2)\Theta(n^2) i både gjennomsnittlig og verste tilfelle. Notasjonen skjuler konstantfaktoren mellom de to siste.

Sjekk deg selv

For enhver input av størrelse nn er kjøretiden til en algoritme Ω(n)+Θ(n2lg⁡n)+O(n3)\Omega(n) + \Theta(n^2 \lg n) + O(n^3). Hva kan du si om kjøretiden W(n)W(n) i verste tilfelle?
Oppdatert 27. september 2026 Foreslå endring