Tilbake
4.4

4.4 Alle-til-alle korteste vei

`Floyd-Warshall` (`d`- og `π`-regel), `Slow-APSP` (min-pluss-produkt) og `Transitive-Closure` — matrisebaserte APSP-algoritmer.

50 min
6 oppgaver
Alle-til-alle korteste vei
Din fremgang i kapitlet
0 / 6 oppgaver

Forkunnskaper

- kap. 4.3 — korteste vei fra én kilde. Dette sto der:
Dijkstra er O(ElgV)O(E\lg V) og krever ikke-negative kantvekter;
Bellman-Ford er Θ(VE)\Theta(VE), tåler negative kanter og oppdager negative
sykler. Begge bygger på slakking: hvis d[u]+w(u,v)<d[v]d[u] + w(u,v) < d[v], er den
nye veien bedre, og d[v]d[v] og π[v]\pi[v] oppdateres.
- kap. 4.1 — grafrepresentasjon. Vi bruker
nabomatrisen her, ikke nabolister: algoritmene i dette kapitlet arbeider
direkte på en V×VV \times V-matrise.
- kap. 1.1 — de asymptotiske symbolene.

Ett nytt begrep innføres underveis: mellomnode. Det er den eneste ideen du
trenger utover kap. 4.3.

Notasjons- og pseudokodeliste

Hvorfor et eget alle-til-alle-problem (~7 min)

En rutetjeneste skal vise reisetiden mellom alle par av holdeplasser, ikke
bare fra én. Den enkle løsningen er å kjøre Dijkstra én gang per node: VV
kjøringer à O(ElgV)O(E\lg V), altså O(VElgV)O(VE\lg V).

For en glissen graf, der EE er omtrent like stor som VV, er det et godt
valg. For en tett graf, der EE nærmer seg V2V^2, blir det O(V3lgV)O(V^3\lg V)
og da finnes noe bedre. Floyd-Warshall gjør jobben i Θ(V3)\Theta(V^3), uten
logaritmefaktor og uten prioritetskø.

Og det finnes en grunn til: Dijkstra krever ikke-negative kantvekter. Har
grafen negative kanter, må du bruke Bellman-Ford per node, og det koster
Θ(V2E)\Theta(V^2E). Floyd-Warshall tåler negative kanter uten videre.

Alle-til-alle korteste vei

problemet med å finne den korteste avstanden mellom hvert par av noder i en
graf, ikke bare fra én bestemt kilde.

Svaret er en V×VV \times V-matrise d med avstander, og gjerne en tilsvarende
matrise π med forgjengere, slik at selve stiene kan hentes ut.

Løsningen er ikke automatisk «kjør én-kilde-algoritmen VV ganger». For
tette grafer og for grafer med negative kanter finnes bedre alternativer, og
valget mellom dem er en fast eksamensoppgave.

Mellomnode

en node som ligger inni en sti — altså verken start- eller endepunkt.

I stien 12341 \to 2 \to 3 \to 4 er mellomnodene 2 og 3.

Hele Floyd-Warshall bygger på dette begrepet: algoritmen utvider trinn
for trinn hvilke noder som får lov til å opptre som mellomnoder, fra ingen til
alle.

Floyd-Warshall: d-regelen og π-regelen (~20 min)

Ideen er å stille ett spørsmål av gangen: hvor korte blir stiene hvis bare
node 1
får være mellomnode? Så: hvis bare node 1 og 2 får det? Og så videre,
til alle nodene er tillatt.

La dij(k)d^{(k)}_{ij} være den korteste avstanden fra ii til jj når bare nodene
1,2,,k1, 2, \dots, k er tillatt som mellomnoder. Da er d(0)d^{(0)} selve
vektmatrisen — ingen mellomnoder er tillatt, så bare direkte kanter teller — og
d(V)d^{(V)} er svaret.

Overgangen fra k1k-1 til kk er ett enkelt valg: enten går den korteste stien
gjennom node kk, eller så gjør den det ikke.

📜Pseudokode-kontrakt: `Floyd-Warshall`
Antagelser om representasjon. Grafen er gitt som en vektmatrise
W[1..n][1..n] med W[i][i] = 0, W[i][j] = w(i,j) når kanten finnes, og
INF ellers. Nodene er nummerert 1 til n=Vn = |V|. Negative kantvekter er
tillatt; negative sykler er ikke.

Prebetingelse: grafen har ingen negativ sykel.
Postbetingelse: d[i][j] er den korteste avstanden fra i til j, og
pi[i][j] er forgjengeren til j på en korteste sti fra i.

Floyd-Warshall(W)
  Input:  vektmatrisen W[1..n][1..n]
  Output: avstandsmatrisen d og forgjengermatrisen pi
  d = W
  for i = 1 to n
      for j = 1 to n
          if i != j and W[i][j] < INF
              pi[i][j] = i
          else
              pi[i][j] = NIL
  for k = 1 to n
      for i = 1 to n
          for j = 1 to n
              if d[i][k] + d[k][j] < d[i][j]
                  d[i][j] = d[i][k] + d[k][j]
                  pi[i][j] = pi[k][j]
  return d, pi
  Kjoeretid: Theta(V^3)

Invarianten i én setning: etter runde k inneholder d[i][j] den korteste
avstanden fra i til j blant alle stier som bare bruker nodene 1 til k
som mellomnoder.

d-regelen og π-regelen er ikke den samme regelen. d tar minimum av
den gamle verdien og veien om k. π beholder den gamle forgjengeren så lenge
den gamle veien er minst like kort — og arver pi[k][j] når veien om k
vinner. Legg merke til at den arvede verdien hentes fra rad k, ikke fra
rad i: den siste kanten inn til j er den samme som på den korteste stien
fra k til j.

Kjøretid: tre nøstede løkker over n=Vn = |V|, med konstant arbeid innerst,
altså Θ(V3)\Theta(V^3). Grensen er tett: løkkene kjører alltid ferdig, uansett
graf.

Rekkefølgen på løkkene er ikke likegyldig. k må være den ytterste.
Bytter du om, brytes invarianten, og resultatet blir galt.

✏️Eksempel 1: To runder av `Floyd-Warshall`

Et internt transportnett har fire terminaler nummerert 1 til 4, med disse
enveis-strekningene og kjøretidene:

121 \to 2 (3), 141 \to 4 (7), 212 \to 1 (8), 232 \to 3 (2), 343 \to 4 (1),
414 \to 1 (2).

a) Skriv opp vektmatrisen W=d(0)W = d^{(0)} og den tilhørende π(0)\pi^{(0)}.
b) Utfør runden med k=1k = 1.
c) Utfør runden med k=2k = 2, og oppgi begge matrisene.

a) Diagonalen er 0, og \infty står der det ikke går noen strekning.

d(0)=Wd^{(0)} = W

1234
103\infty7
2802\infty
3\infty\infty01
42\infty\infty0

π(0)\pi^{(0)}
1234
1NIL1NIL1
22NIL2NIL
3NILNILNIL3
44NILNILNIL

I π(0)\pi^{(0)} står i selv som forgjenger overalt der det finnes en direkte
kant — den korteste kjente stien fra i til j er da kanten, og forgjengeren
til j er i.

b) Med k=1k = 1 ser vi etter stier i1ji \to 1 \to j som er kortere enn det vi

har. To celler forbedres:
- d[2][4]d[2][4]: veien 2142 \to 1 \to 4 koster 8+7=158 + 7 = 15, mot \infty før.
Forgjengeren arves fra rad 1: π[2][4]=π[1][4]=1\pi[2][4] = \pi[1][4] = 1.
- d[4][2]d[4][2]: veien 4124 \to 1 \to 2 koster 2+3=52 + 3 = 5, mot \infty før.
π[4][2]=π[1][2]=1\pi[4][2] = \pi[1][2] = 1.
d(1)d^{(1)}

1234
103\infty7
280215
3\infty\infty01
425\infty0

c) Med k=2k = 2 ser vi etter stier i2ji \to 2 \to j. To celler forbedres:
- d[1][3]d[1][3]: veien 1231 \to 2 \to 3 koster 3+2=53 + 2 = 5, mot \infty før.

π[1][3]=π[2][3]=2\pi[1][3] = \pi[2][3] = 2.
- d[4][3]d[4][3]: veien 4234 \to 2 \to 3 koster 5+2=75 + 2 = 7, mot \infty før.

π[4][3]=π[2][3]=2\pi[4][3] = \pi[2][3] = 2.
Sluttilstanden — det du ville levert på eksamen:
d(2)d^{(2)}

1234
10357
280215
3\infty\infty01
42570

π(2)\pi^{(2)}

1234
1NIL121
22NIL21
3NILNILNIL3
4412NIL

Legg merke til tre ting.
Alle cellene som ikke nevnes, står uendret fra forrige runde — i begge
matrisene. En vanlig feil er å regne om hele matrisen fra grunnen av.
Forgjengeren arves fra rad k, ikke fra rad i. Ved d[4][3]d[4][3] hentet vi
π[2][3]\pi[2][3], ikke π[4][2]\pi[4][2].

Rad 3 er uendret gjennom begge rundene. Node 3 har bare én utgående kant, til

node 4, og hverken node 1 eller 2 kan nås derfra på to runder.
Fellen her er å blande d-regelen og π-regelen. d tar et minimum;
π gjør et valg mellom to forgjengere, og valget følger utfallet av
minimumet.

📝Oppgave 1

(Innstegsoppgave, sjanger D — definisjon med egne ord, altså én presis setning
med hovedpoenget først.)

Hva betyr dij(k)d^{(k)}_{ij} i Floyd-Warshall, og hva er d(0)d^{(0)}?

📝Oppgave 2
Eksamensnivå, sjanger C

Bruk grafen fra Eksempel 1, og fortsett fra d(2)d^{(2)}.

Utfør runden med k=3k = 3, og oppgi bare de cellene i dd som endrer seg,
med de nye verdiene og de nye forgjengerne.

📝Oppgave 3
Eksamensnivå, sjanger F

Ta stilling til hver av påstandene om Floyd-Warshall:

a) Løkkene kan kjøres i hvilken som helst rekkefølge, siden alle tre går
over alle nodene.
b) Algoritmen krever ikke-negative kantvekter.
c) Kjøretiden er Θ(V3)\Theta(V^3) uansett hvor mange kanter grafen har.

Slow-APSP og min-pluss-produktet (~14 min)

Det finnes en helt annen vei til det samme svaret, og den er verdt å kunne
fordi den kommer som en egen håndkjøringsoppgave: å bygge stiene opp kant for
kant
i stedet for mellomnode for mellomnode.

La lij(m)l^{(m)}_{ij} være den korteste avstanden fra ii til jj blant stier med
høyst mm kanter. Da er l(1)=Wl^{(1)} = W, og skrittet videre er å legge til én
kant til.

Min-pluss-produktet
operasjonen som forlenger alle stier med én kant:

lij(m)=min1kn(lik(m1)+wkj)l^{(m)}_{ij} = \min_{1 \le k \le n}\left(l^{(m-1)}_{ik} + w_{kj}\right)

Den ser ut som et matriseprodukt, men med minimum der produktet har sum, og
pluss der det har gange.

Dette er kapitlets viktigste felle. Vanlig matriseprodukt ville vært
klikwkj\sum_k l_{ik}\cdot w_{kj} — sum av produkter. Her er det minimum av summer, og
et svar regnet ut med vanlig produkt er fullstendig meningsløst i
grafsammenheng.

📜Pseudokode-kontrakt: `Slow-APSP`
Antagelser om representasjon. Vektmatrisen W[1..n][1..n] som over.
L er en matrise av samme form, med L[i][j] = korteste avstand med høyst et
visst antall kanter.

Prebetingelse: ingen negativ sykel.
Postbetingelse: rutinen returnerer matrisen der hver sti er forlenget med
høyst én kant til.

Slow-APSP(L, W)
  Input:  matrisene L og W, begge n x n
  Output: matrisen L' der stiene er forlenget med hoeyst en kant
  la L' vaere en ny n x n matrise
  for i = 1 to n
      for j = 1 to n
          L'[i][j] = INF
          for k = 1 to n
              L'[i][j] = min(L'[i][j], L[i][k] + W[k][j])
  return L'
  Kjoeretid: Theta(V^3) per produkt

Invarianten i én setning: er L korteste avstander med høyst mm kanter,
er L' korteste avstander med høyst m+1m+1 kanter.

Hele APSP-problemet løses ved å gjenta produktet. En korteste sti i en graf
uten negative sykler har høyst V1V-1 kanter, så V2V-2 produkter etter det
første holder — altså Θ(V4)\Theta(V^4) naivt. Med gjentatt kvadrering kommer man
ned i Θ(V3lgV)\Theta(V^3\lg V), men Floyd-Warshall med sine Θ(V3)\Theta(V^3) er
fortsatt bedre.

Kjøretid: Θ(V3)\Theta(V^3) for ett produkt, Θ(V4)\Theta(V^4) for hele den
naive løsningen.

✏️Eksempel 2: Én celle i min-pluss-produktet

Bruk den samme grafen som i Eksempel 1, med vektmatrisen WW.

Regn ut l13(2)l^{(2)}_{13}, altså den korteste avstanden fra node 1 til node 3 med
høyst to kanter. Vis alle leddene.

Vi bruker l(1)=Wl^{(1)} = W og regner

l13(2)=min1k4(l1k(1)+wk3)l^{(2)}_{13} = \min_{1 \le k \le 4}\left(l^{(1)}_{1k} + w_{k3}\right)

kkl1k(1)l^{(1)}_{1k}wk3w_{k3}Summen
100\infty\infty
2332255
3\infty00\infty
477\infty\infty

Svaret: l13(2)=5l^{(2)}_{13} = 5, via k=2k = 2, altså stien 1231 \to 2 \to 3.
Legg merke til de tre \infty-leddene. Regelen +x=\infty + x = \infty
gjelder, og de bidrar ikke til minimumet. Leddet med k=1k = 1 er 0+0 + \infty
fordi det ikke finnes noen kant 131 \to 3; leddet med k=4k = 4 er 7+7 + \infty
fordi det ikke finnes noen kant 434 \to 3.

Kontrollen mot vanlig matriseprodukt. Med vanlig produkt ville vi regnet

kW1kWk3\sum_k W_{1k}\cdot W_{k3}, altså 0+32+0+70\cdot\infty + 3\cdot 2 + \infty\cdot 0 + 7\cdot\infty. Det gir ingen mening som avstand, og det er ikke det oppgaven
spør etter — men det er nøyaktig den feilen som gjøres når man leser
«matriseprodukt» og går på autopilot.
Svarformen på eksamen: ett tall. Ba oppgaven om hele l(2)l^{(2)}, er det

matrisen. Ba den om én celle, er det tallet og ikke mer.

📝Oppgave 4
Eksamensnivå, sjanger C

Bruk grafen fra Eksempel 1.

a) Regn ut l24(2)l^{(2)}_{24}, altså korteste avstand fra node 2 til node 4 med
høyst to kanter.
b) Regn ut l34(2)l^{(2)}_{34}.

Transitive-Closure og utskrift av stien (~9 min)

Noen ganger er ikke avstanden interessant — bare om det i det hele tatt går an
å komme fra ii til jj. Da holder det med sanne og usanne verdier, og
algoritmen blir billigere i praksis fordi den slipper aritmetikk.

`Transitive-Closure`

den boolske varianten av Floyd-Warshall: tij(k)t^{(k)}_{ij} er sann hvis det
finnes en vei fra ii til jj som bare bruker nodene 1 til kk som mellomnoder.

Regelen er
tij(k)=tij(k1)(tik(k1)tkj(k1))t^{(k)}_{ij} = t^{(k-1)}_{ij} \lor \left(t^{(k-1)}_{ik} \land t^{(k-1)}_{kj}\right)
— «eller» der d-regelen har «minimum», «og» der den har «pluss».

Kjøretid Θ(V3)\Theta(V^3).

Startmatrisen t(0)t^{(0)} har sann på diagonalen og der det finnes en kant.
Enhver node når seg selv med null kanter.

`Print-All-Pairs-Shortest-Path`

skriver ut selve stien fra ii til jj ved å lese forgjengermatrisen
baklengs: siste node er jj, forgjengeren er π[i][j]\pi[i][j], og så videre til
du treffer ii.

Kjøretid O(V)O(V) per sti — stien kan ikke ha flere enn VV noder.

Dette er grunnen til at π-matrisen finnes. d gir bare tallet; π gir
veien. Ber en oppgave om stien, er det π\pi du skal bruke, ikke dd.

📝Oppgave 5
Eksamensnivå, sjanger C…

Bruk π(2)\pi^{(2)} fra Eksempel 1.

a) Hvilken sti fra node 4 til node 3 er kodet i matrisen på dette
tidspunktet?
b) Forklar med én setning hvorfor d-matrisen alene ikke er nok til å
svare på a).

📝Oppgave 6
Eksamensnivå, sjanger E…

En kartleverandør skal regne ut reisetid mellom alle par av VV steder.

a) Grafen er glissen (EVE \approx V) og alle vektene er positive. Hvilken
algoritme velger du, og hva blir kjøretiden?
b) Grafen er tett (EV2E \approx V^2) og noen vekter er negative, men det
finnes ingen negativ sykel. Hvilken velger du, og hva blir kjøretiden?
c) Bare spørsmålet «finnes det en vei?» skal besvares, for alle par.
Hvilken velger du?

Kjøretidene samlet

Dette er kapitlets puggeflate. Eksamen er hjelpemiddelfri, så tabellen må ligge
i hodet.

AlgoritmeKjøretidKrav / egenskap
Floyd-WarshallΘ(V3)\Theta(V^3)tåler negative kanter; ingen negativ sykel; k ytterst
Slow-APSP, ett produktΘ(V3)\Theta(V^3)min-pluss, ikke vanlig matriseprodukt
Slow-APSP, hele problemet naivtΘ(V4)\Theta(V^4)V2V-2 produkter etter det første
Transitive-ClosureΘ(V3)\Theta(V^3)boolsk: «eller» og «og» i stedet for min og pluss
Print-All-Pairs-Shortest-PathO(V)O(V) per stileser π baklengs
VV kjøringer av DijkstraO(VElgV)O(VE\lg V)krever ikke-negative vekter; best på glisne grafer
VV kjøringer av Bellman-FordΘ(V2E)\Theta(V^2E)tåler negative kanter, men dyrt

Én presisering som er verdt å ta med seg. Floyd-Warshall bryr seg ikke om
hvor mange kanter grafen har — kjøretiden er Θ(V3)\Theta(V^3) uansett. Det er både
styrken (tette grafer) og svakheten (svært glisne grafer, der VV kjøringer av
Dijkstra er raskere).

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp
har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet
gjelder kjernestoffet.

`Floyd-Warshall`

løser alle-til-alle korteste vei ved å prøve hver node etter tur som tillatt
mellomnode, og oppdatere hele avstandsmatrisen for én kk om gangen.

Kjøretid Θ(V3)\Theta(V^3), uavhengig av antall kanter.

Tåler negative kantvekter, men ikke negative sykler. k må være den
ytterste løkka.

`d`-regelen
dij(k)=min(dij(k1),  dik(k1)+dkj(k1))d^{(k)}_{ij} = \min\left(d^{(k-1)}_{ij},\; d^{(k-1)}_{ik} + d^{(k-1)}_{kj}\right)

Er veien om node kk kortere enn den vi hadde, brukes den.

Celler der veien om kk ikke er kortere, står uendret — de skal ikke regnes
om.

`π`-regelen

forgjengeren beholdes når den gamle veien er minst like kort, og arves fra
rad kk når veien om kk vinner: πij(k)=πkj(k1)\pi^{(k)}_{ij} = \pi^{(k-1)}_{kj}.

Oppdateres i samme løkke som d, men etter en annen regel.

Fellen er å arve fra rad ii. Den siste kanten inn til jj er den samme som
på korteste sti fra kk til jj, og det er derfor rad kk som gjelder.

Mellomnode

en node som ligger inni en sti, altså verken start- eller endepunkt.

d(k)d^{(k)} er avstandene når bare nodene 1 til kk er tillatt som mellomnoder.

Hele Floyd-Warshalls invariant er formulert i dette begrepet.

Min-pluss-produktet
lij(m)=mink(lik(m1)+wkj)l^{(m)}_{ij} = \min_k\left(l^{(m-1)}_{ik} + w_{kj}\right) — minimum av summer.

Forlenger alle stier med høyst én kant til.

Ikke vanlig matriseprodukt. Vanlig produkt ville vært sum av produkter, og
gir ingen mening som avstand.

`Slow-APSP`

bygger korteste stier opp kant for kant ved gjentatte min-pluss-produkter.

Θ(V3)\Theta(V^3) per produkt, Θ(V4)\Theta(V^4) for hele problemet naivt.

Erstattes i praksis av Floyd-Warshall, som er Θ(V3)\Theta(V^3) totalt — men
min-pluss-regningen kommer som egen håndkjøringsoppgave.

lij(m)l^{(m)}_{ij}

korteste avstand fra ii til jj blant stier med høyst mm kanter.

l(1)=Wl^{(1)} = W, og l(V1)l^{(V-1)} er svaret når grafen ikke har negative sykler.

«Høyst», ikke «nøyaktig». Diagonalleddene wii=0w_{ii} = 0 er det som gjør
kortere stier lovlige.

`Transitive-Closure`

den boolske varianten: finnes det i det hele tatt en vei fra ii til jj?

tij(k)=tij(k1)(tik(k1)tkj(k1))t^{(k)}_{ij} = t^{(k-1)}_{ij} \lor (t^{(k-1)}_{ik} \land t^{(k-1)}_{kj}),
kjøretid Θ(V3)\Theta(V^3).

Startmatrisen har sann på diagonalen — enhver node når seg selv.

`Print-All-Pairs-Shortest-Path`

skriver ut selve stien ved å lese π\pi baklengs fra jj til ii.

Kjøretid O(V)O(V) per sti.

d gir lengden, π gir veien. Ber oppgaven om stien, er det π\pi som
brukes.

Vektmatrisen WW
W[i][j]=w(i,j)W[i][j] = w(i,j) når kanten finnes, W[i][i]=0W[i][i] = 0, og \infty ellers.

Er samtidig d(0)d^{(0)} i Floyd-Warshall og l(1)l^{(1)} i Slow-APSP.

Regelen +x=\infty + x = \infty gjelder i all regning på denne matrisen.

Forgjengermatrisen π\pi
π[i][j]\pi[i][j] er forgjengeren til jj på en korteste sti fra ii.

NIL betyr enten at i=ji = j, eller at ingen vei er funnet.

Fra π\pi kan hele stien leses ut baklengs i O(V)O(V).

Glissen mot tett graf

en graf er glissen når EE er omtrent som VV, og tett når EE nærmer
seg V2V^2.

For glisne grafer er VV kjøringer av DijkstraO(VElgV)O(VE\lg V) — best. For
tette er Floyd-Warshall med Θ(V3)\Theta(V^3) best.

Tettheten er halve svaret på «hvilken algoritme velger du»; fortegnet på
vektene er den andre halvparten.

Felle #8 — `Dijkstra` på negative kanter
Dijkstra forutsetter ikke-negative kantvekter og kan gi feil svar med én
eneste negativ kant.

Floyd-Warshall og Bellman-Ford tåler negative kanter.

Sjekk fortegnet før du velger algoritme — det er det første spørsmålet i en
korteste-vei-oppgave.

Negativ sykel

en sykel der summen av kantvektene er negativ.

Da finnes ingen korteste sti: du kan gå rundt sykelen igjen og igjen og få
lavere og lavere kostnad.

Floyd-Warshall forutsetter at det ikke finnes noen. Bellman-Ford
oppdager dem, og det er dens særegne styrke.

Sjanger C — håndkjøring

oppgavetypen der du utfører algoritmen steg for steg og oppgir sluttilstanden.

Her er svarformen den etterspurte matrisen for den etterspurte runden
eller den ene cellen, hvis det er det som er spurt om.

Ikke lever både d og π når bare én var etterspurt.

Sjanger E — kjøretidskunnskap

oppgavetypen der du oppgir kjøretiden til en navngitt algoritme.

Θ(V3)\Theta(V^3) for Floyd-Warshall og Transitive-Closure, Θ(V4)\Theta(V^4) for
naiv Slow-APSP.

Merk at antall kanter ikke inngår i noen av dem — det er poenget med
matriseformuleringen.

Repetisjonsoppgaver

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.