6.2 Differenslikninger

Løse (koblede) differenslikninger ved å allokere arrays, sette startverdier og oppdatere neste indeks i løkke — med kritisk fokus på **oppdateringsrekkefølge**.

55 min
12 oppgaver
Differenslikninger
Din fremgang i kapitlet
0 / 12 oppgaver

Sist du var her

Dette kapitlet bruker tre ting fra Del 1 og Del 4 om og om igjen. Her er de, ferdig oppfrisket med kode og utskrift, slik at du ikke trenger å bla.

1) En tom tabell av gitt lengde (kap. 4.1). np.zeros(n) lager n plasser fylt med nuller — lagerplassen vi skal skrive resultatene inn i:

import numpy as np

x = np.zeros(4)
print(x)
print(len(x))

Utskrift:

[0. 0. 0. 0.]
4

2) Hvor mange runder gir range(N)? (kap. 1.3) Nøyaktig N runder, med n fra 0 til N-1. Legg merke til hva n+1 blir i siste runde:

for n in range(3):
    print(n, n+1)

Utskrift:

0 1
1 2
2 3

3) En jevn tidsakse (kap. 4.1). np.linspace(a, b, N+1) gir N+1 punkter med begge endepunktene med, altså N like store steg:

import numpy as np

t = np.linspace(0, 6, 4)
print(t)
print(t[1] - t[0])

Utskrift:

[0. 2. 4. 6.]
2.0

De tre bitene er hele verktøykassen. Resten av kapitlet er å bruke dem i samme faste mønster.

Forkunnskaper

- NumPy-tabeller, np.zeros og np.linspace: kap. 4.1.
- Løkker og range: kap. 1.3.
- Funksjoner som returnerer flere verdier: kap. 1.6.
- Plotting med legende: kap. 4.3 — brukes i den siste løkka og i alle b)-deloppgavene.
- f-strenger til pen utskrift: kap. 1.4.

Matematikken bak er ikke pensum her, men vil du se hvor likningene kommer fra, ligger det korte veien i Differensialligninger: separable og lineære første ordens og Differensiallikningssystem x′=Ax og diskret dynamikk.

Kapitlet er delt i tre løkker med tidsanslag.

Løkke 1 — én differenslikning (~18 min)

En biolog følger en bakteriekultur og teller antallet hver time. Hun har ingen formel for antallet ved time 37. Det hun har, er en regel: hvor mye kulturen vokser den neste timen, avhenger av hvor stor den er nå, og av hvor nær den er grensen for hva næringen tåler.

En slik regel — «neste verdi uttrykt ved den forrige» — er en differenslikning. Den er langt vanligere i vitenskapelig programmering enn en ferdig formel, fordi de fleste virkelige prosesser er lettere å beskrive skritt for skritt enn å løse eksakt.

Og det passer datamaskinen perfekt: en regel som sier hva som skjer i ett skritt, er akkurat det en løkke gjør.

Differenslikning
En regel som gir neste verdi i en tallfølge ut fra den forrige, sammen med en startverdi. Skrevet ut:

xn+1=F(xn),x0 gittx_{n+1} = F(x_n), \qquad x_0 \text{ gitt}

Indeksen nn teller skritt, ikke tid direkte: x0x_0 er verdien du starter med, x1x_1 verdien etter ett skritt, og så videre. Uten startverdien er likningen ubrukelig — den sier hvordan du går videre, ikke hvor du står.

Et eksempel med F(x)=0,5x+3F(x) = 0{,}5x + 3 og x0=12x_0 = 12:

import numpy as np

x = np.zeros(6)
x[0] = 12.0
for n in range(5):
    x[n+1] = 0.5*x[n] + 3
print(x)

Utskrift:

[12.      9.      7.5     6.75    6.375   6.1875]
📜Allokeringsmønsteret — malen for én differenslikning

Dette er bokas faste mal for sjanger H med én likning. Fire steg, alltid i denne rekkefølgen:

1. Allokér tabellen med plass til alle verdiene: x = np.zeros(N+1). Å allokere betyr å be om lagerplassen på forhånd, før du vet hva som skal stå der.
2. Sett startverdien før løkka: x[0] = x0.
3. Løkke over skrittene, ikke over punktene: for n in range(N): og oppdater x[n+1].
4. Returnér tabellen.

import numpy as np

def simuler(x0, N):
    x = np.zeros(N+1)
    x[0] = x0
    for n in range(N):
        x[n+1] = 0.5*x[n] + 3
    return x

print(simuler(12.0, 3))

Utskrift:

[12.    9.    7.5   6.75]

Tellingen er hele hemmeligheten: N skritt gir N+1 verdier, fordi startverdien også er en verdi. range(N) kjører med n fra 0 til N-1, så siste tilordning er til x[N] — det siste elementet i en tabell med N+1 plasser. Alt passer nøyaktig, og det er ingen tilfeldighet: malen er bygd slik.

✏️Eksempel 1: bakteriekulturen som møter en grensen for næring

En bakteriekultur vokser med 40 % per time når det er god plass, men veksten bremser jo nærmere bestanden kommer det næringen tåler, som er 500 millioner celler. Modellen er xn+1=xn+rxn(1xnK)\displaystyle x_{n+1} = x_n + r\,x_n\left(1 - \frac{x_n}{K}\right) med r=0,4r = 0{,}4 og K=500K = 500. Kulturen starter på 20 millioner celler. Skriv logistisk(r, K, x0, N) og vis bestanden etter 0, 1, 2, 10 og 20 timer.


Malen fra teoremet over, med den oppgitte høyresiden satt inn på linja som oppdaterer x[n+1]:

import numpy as np

def logistisk(r, K, x0, N):
    x = np.zeros(N+1)
    x[0] = x0
    for n in range(N):
        x[n+1] = x[n] + r*x[n]*(1 - x[n]/K)
    return x

x = logistisk(0.4, 500.0, 20.0, 20)
print('antall verdier:', len(x))
for n in [0, 1, 2, 10, 20]:
    print(f'time {n:2d}: {x[n]:8.2f}')

Utskrift:

antall verdier: 21
time  0:    20.00
time  1:    27.68
time  2:    38.14
time 10:   306.49
time 20:   497.44

Les tallene: veksten er beskjeden i starten (7,68 millioner den første timen), er på sitt kraftigste midtveis, og bremser opp mot 500 — grensen KK. Etter 20 timer er kulturen på 497,44, altså praktisk talt i ro.

Legg merke til len(x), som er 21 og ikke 20. Tjue timer gir tjue skritt, og tjue skritt gir 21 verdier fordi starttilstanden også teller. Det er verdt å skrive ut en gang, for det er nøyaktig denne tellingen som ryker under tidspress.

📝Oppgave 1

(Innstegsoppgave, sjanger A — kodesporing, altså «hva skriver dette programmet ut?».) Skriv utskriften nøyaktig slik Python viser den.

import numpy as np

x = np.zeros(4)
x[0] = 1.0
for n in range(3):
    x[n+1] = 2*x[n] + 1
print(x)
print(x.size)

Startverdien settes før løkka

Linja x[0] = x0 skal stå mellom allokeringen og løkka. Grunnen er at løkka bare produserer x[1] og oppover: den regner alltid en ny verdi ut fra en som allerede finnes, og den aller første må derfor komme utenfra.

Glemmer du linja, får du ingen feilmelding. np.zeros har allerede fylt tabellen med nuller, så programmet regner videre fra 0 og gir et helt forløp som er feil, men som ser respektabelt ut. Det er den dyreste varianten av feilen.

import numpy as np

x = np.zeros(4)
for n in range(3):
    x[n+1] = 2*x[n] + 1
print(x)

Utskrift:

[0. 1. 3. 7.]

Sammenlign med forrige oppgave: alle tallene har flyttet seg én plass, fordi forløpet startet på 0 i stedet for på 1.

Likevektsverdi

En verdi som gjentar seg selv: setter du den inn i høyresiden, kommer den samme verdien ut igjen. For xn+1=F(xn)x_{n+1} = F(x_n) er likevekten løsningen av x=F(x)x = F(x).

Dette er den enkleste kontrollen du kan gjøre på en simulering: regn ut likevekten for hånd, og se om tallene i tabellen nærmer seg den. Gjør de ikke det, er det som regel en feil i koden — ikke i modellen.

For xn+1=0,5xn+3x_{n+1} = 0{,}5x_n + 3 gir x=0,5x+3x = 0{,}5x + 3 likevekten x=6x = 6. Utskriften i eksemplene over gikk 12, 9, 7,5, 6,75, 6,375, 6,1875 — nedover mot nettopp 6. For den logistiske modellen er likevekten KK, altså 500, og tabellen sluttet på 497,44.

📝Oppgave 2
Eksamensnivå, sjanger H
Et legemiddel brytes ned slik at 20 % forsvinner fra blodet i løpet av et døgn. Pasienten får samme dose hvert døgn, og dosen tilsvarer 5 enheter. Konsentrasjonen rett etter dose n+1n+1 er da

cn+1=0,8cn+5c_{n+1} = 0{,}8\,c_n + 5

Behandlingen starter fra null: c0=0c_0 = 0.

a) Skriv konsentrasjon(c0, N) som returnerer tabellen med de N+1N+1 verdiene.

b) Skriv ut konsentrasjonen etter første, andre og tiende dose.

c) Regn ut likevektsverdien for hånd, og sammenlign med tallet i b).

Løkke 2 — to koblede likninger og rekkefølgen (~22 min)

— naturlig pausepunkt —

Nå kommer det som faktisk er eksamensoppgaven. I nesten alle settene siden 2022 har sjanger H hatt to størrelser som påvirker hverandre: to bestander, en posisjon og en fart, en tykkelse og en strømning.

Malen utvides på den opplagte måten — to tabeller, to startverdier, to oppdateringslinjer i samme løkke. Men det dukker opp et spørsmål som ikke fantes med bare én likning: hvilken av de to linjene skal stå først?

Svaret leser du rett ut av likningene. Står det yn+1y_{n+1} på høyresiden i likningen for xn+1x_{n+1}, må y[n+1] regnes ut først. Gjør du det motsatt, henter Python en verdi som ennå ikke er beregnet — og siden tabellen ble laget med np.zeros, står det en null der, uten et eneste varsel.

📜Malen for to koblede differenslikninger

Utvidelsen av allokeringsmønsteret til to størrelser. Seks steg:

1. Allokér begge tabellene: x = np.zeros(N+1) og y = np.zeros(N+1).
2. Sett begge startverdiene: x[0] = x0, y[0] = y0.
3. Én løkke, for n in range(N): — ikke to løkker etter hverandre.
4. Les likningene og bestem rekkefølgen: den likningen som bruker den andres nye verdi, skal stå sist.
5. Oppdater begge inne i samme runde.
6. return x, y — begge tabellene, i den rekkefølgen oppgaven navngir dem.

x = np.zeros(N+1)
y = np.zeros(N+1)
x[0] = x0
y[0] = y0
for n in range(N):
    y[n+1] = ...      # denne trengs av den neste
    x[n+1] = ...      # denne bruker y[n+1]
return x, y

Blokken over er et fragment, ikke et kjørbart program — den mangler import og en funksjon rundt seg, og prikkene skal erstattes av de faktiske uttrykkene. Den står her som mal, ikke som eksempel til å kjøre.

Punkt 4 er hele oppgaven. Er de to likningene ikke koblet gjennom nye verdier — altså bruker begge bare x[n] og y[n] — spiller rekkefølgen ingen rolle, og da skriver du dem i den rekkefølgen oppgaven gir dem.

✏️Eksempel 2: seismometeret — når rekkefølgen er bestemt av likningene
En seismometermasse henger i en fjær. Posisjonen xx og farten vv oppdateres etter

vn+1=vnΔtkxn,xn+1=xn+Δtvn+1v_{n+1} = v_n - \Delta t\,k\,x_n, \qquad x_{n+1} = x_n + \Delta t\,v_{n+1}

med k=1k = 1, Δt=0,1\Delta t = 0{,}1, x0=1x_0 = 1 og v0=0v_0 = 0. Skriv seismometer(k, dt, x0, v0, N) som returnerer begge tabellene, og skriv ut de fem første skrittene.


Les den andre likningen: den bruker vn+1v_{n+1}, altså den nye farten. Da må v[n+1] regnes ut først. Rekkefølgen er ikke en smakssak her — den står i oppgaven.

import numpy as np

def seismometer(k, dt, x0, v0, N):
    x = np.zeros(N+1)
    v = np.zeros(N+1)
    x[0] = x0
    v[0] = v0
    for n in range(N):
        v[n+1] = v[n] - dt*k*x[n]
        x[n+1] = x[n] + dt*v[n+1]
    return x, v

x, v = seismometer(1.0, 0.1, 1.0, 0.0, 5)
for n in range(6):
    print(f'n = {n}   x = {x[n]:8.4f}   v = {v[n]:8.4f}')

Utskrift:

n = 0   x =   1.0000   v =   0.0000
n = 1   x =   0.9900   v =  -0.1000
n = 2   x =   0.9701   v =  -0.1990
n = 3   x =   0.9405   v =  -0.2960
n = 4   x =   0.9015   v =  -0.3901
n = 5   x =   0.8535   v =  -0.4802

Massa slippes i ro i posisjon 1, får negativ fart (den trekkes mot null), og posisjonen synker. Alt oppfører seg som en masse i en fjær skal.

Merk hvordan de to linjene inne i løkka leses: v[n+1] bruker bare gamle verdier, mens x[n+1] bruker den ferske v[n+1] som ble regnet ut på linja over. Det er nøyaktig det likningene ba om.

Oppdateringsrekkefølge

Regelen for hvilken linje som skal stå først inne i løkka: den likningen hvis høyreside inneholder en verdi med indeks n+1n+1, skal stå etter den likningen som produserer den verdien.

Oppskriften i tre trinn, som du kan gjøre på papiret før du skriver en eneste kodelinje:

1. Skriv opp begge likningene.
2. Ring rundt alle ledd med indeks n+1n+1høyre side.
3. Den ringede verdien må være beregnet før linja brukes — sett den linja først.

Bruker ingen av høyresidene en n+1n+1-verdi, er systemet ukoblet i tid, og rekkefølgen er likegyldig.

Grunnen til at dette er farlig i Python, er at np.zeros allerede har lagt en 0 på hver plass. Leser du v[n+1] før den er skrevet, får du 0 i stedet for en feilmelding, og programmet fortsetter som om ingenting.

✏️Eksempel 3: samme kode med linjene byttet om

Bytt om de to linjene inne i løkka i eksempel 2, slik at x[n+1] regnes først, og se hva som skjer. Alt annet holdes likt.


Eneste endring er rekkefølgen på de to linjene inne i løkka:

import numpy as np

def gal_rekkefolge(k, dt, x0, v0, N):
    x = np.zeros(N+1)
    v = np.zeros(N+1)
    x[0] = x0
    v[0] = v0
    for n in range(N):
        x[n+1] = x[n] + dt*v[n+1]
        v[n+1] = v[n] - dt*k*x[n]
    return x, v

x, v = gal_rekkefolge(1.0, 0.1, 1.0, 0.0, 5)
for n in range(6):
    print(f'n = {n}   x = {x[n]:8.4f}   v = {v[n]:8.4f}')

Utskrift:

n = 0   x =   1.0000   v =   0.0000
n = 1   x =   1.0000   v =  -0.1000
n = 2   x =   1.0000   v =  -0.2000
n = 3   x =   1.0000   v =  -0.3000
n = 4   x =   1.0000   v =  -0.4000
n = 5   x =   1.0000   v =  -0.5000

Posisjonen står bom stille på 1,0000 for alltid, samtidig som farten vokser og vokser. En masse som har fart, men ikke flytter seg, finnes ikke.

Forklaringen: på linja x[n+1] = x[n] + dt*v[n+1] er v[n+1] ennå ikke beregnet, så Python henter det som står der — nullen fra np.zeros. Da blir x[n+1] = x[n] + dt*0, altså uendret. Ingen feilmelding, ingen advarsel, bare tall som er stille gale.

Dette er grunnen til at du bør se på tallene og spørre «kan dette stemme fysisk?» før du leverer. En størrelse som ikke rører seg i det hele tatt, er alltid mistenkelig.

📝Oppgave 3

(Midtveisnivå, sjanger A — kodesporing.) Hva skriver programmet ut? Skriv begge tabellene nøyaktig slik Python viser dem.

import numpy as np

a = np.zeros(4)
b = np.zeros(4)
a[0] = 2.0
b[0] = 1.0
for n in range(3):
    b[n+1] = b[n] + a[n]
    a[n+1] = a[n] + b[n+1]
print(a)
print(b)

📝Oppgave 4
Eksamensnivå, sjanger H
En isbre beskrives med tykkelsen hh i meter og strømningshastigheten uu i meter per år. Modellen er

un+1=un+Δt(αhnβun),hn+1=hn+Δt(aun+1)u_{n+1} = u_n + \Delta t\,(\alpha h_n - \beta u_n), \qquad h_{n+1} = h_n + \Delta t\,(a - u_{n+1})

der a=12a = 12 er den årlige snøtilførselen. Bruk α=0,5\alpha = 0{,}5, β=0,8\beta = 0{,}8, Δt=0,1\Delta t = 0{,}1, h0=30h_0 = 30 og u0=5u_0 = 5.

a) Avgjør hvilken av de to linjene som må stå først inne i løkka, og begrunn det med ett ord fra likningene.

b) Skriv isbre(alfa, beta, a, dt, h0, u0, N) som returnerer h, u.

c) Kjør med N=400N = 400 og skriv ut verdiene for n=0,1,2,100n = 0, 1, 2, 100 og 400400. Hva nærmer de seg?

📝Oppgave 5
Eksamensnivå, sjanger O

Programmet under skal simulere isbreen fra forrige oppgave, men det virker ikke. Finn de tre feilene, forklar hva hver av dem fører til, og skriv den rettede koden.

import numpy as np

def simuler(alfa, beta, dt, h0, u0, N):
    h = np.zeros(N)
    u = np.zeros(N)
    h[0] = h0
    for n in range(N):
        h[n+1] = h[n] + dt*(12 - u[n+1])
        u[n+1] = u[n] + dt*(alfa*h[n] - beta*u[n])
    return h, u

h, u = simuler(0.5, 0.8, 0.1, 30.0, 5.0, 5)
print(h)

Løkke 3 — tid, plott og fasekurve (~15 min)

Differenslikningen teller skritt, ikke tid. Skal figuren ha en fornuftig førsteakse, må du lage tidsaksen selv — og det er alltid del b) av eksamensoppgaven.

Den andre halvdelen av del b) er valget mellom to figurtyper. Enten plotter du begge størrelsene mot tid, i samme figur med hver sin legende, eller du plotter den ene mot den andre. Det siste kalles en fasekurve, og den viser noe helt annet enn tidsplottet: den viser hvilke kombinasjoner systemet besøker.

Tidsaksen til en differenslikning

Skrittene n=0,1,,Nn = 0, 1, \ldots, N svarer til tidspunktene tn=nΔtt_n = n\,\Delta t. Tabellen med tidspunkter lages på én linje, og den må ha nøyaktig like mange elementer som verdiene, ellers klager plottefunksjonen:

import numpy as np

dt = 0.1
N = 5
t = np.linspace(0, N*dt, N+1)
print(t)
print(len(t))

Utskrift:

[0.  0.1 0.2 0.3 0.4 0.5]
6

Merk at sluttiden er N*dt, ikke N, og at antallet er N+1 — samme telling som for verditabellene. Alternativet t = np.arange(N+1)*dt gir det samme, men linspace med N+1 er bokas form fordi den gjør tellingen synlig.

✏️Eksempel 4: rovdyr og byttedyr som differenslikning
Bestandene av byttedyr BB og rovdyr RR (i tusen) følger

Bn+1=Bn+Δt(aBnbBnRn),Rn+1=Rn+Δt(cBnRndRn)B_{n+1} = B_n + \Delta t\,(aB_n - bB_nR_n), \qquad R_{n+1} = R_n + \Delta t\,(cB_nR_n - dR_n)

med a=0,4a = 0{,}4, b=0,02b = 0{,}02, c=0,01c = 0{,}01, d=0,3d = 0{,}3, Δt=0,01\Delta t = 0{,}01, B0=40B_0 = 40 og R0=9R_0 = 9. Simulér 60 år og vis bestandene hvert tiende år.


Her bruker ingen av høyresidene en verdi med indeks n+1n+1: begge er uttrykt i BnB_n og RnR_n alene. Rekkefølgen inne i løkka er derfor likegyldig, og vi skriver dem i den rekkefølgen oppgaven gir.

import numpy as np

def rovdyr_byttedyr(a, b, c, d, dt, B0, R0, N):
    B = np.zeros(N+1)
    R = np.zeros(N+1)
    B[0] = B0
    R[0] = R0
    for n in range(N):
        B[n+1] = B[n] + dt*(a*B[n] - b*B[n]*R[n])
        R[n+1] = R[n] + dt*(c*B[n]*R[n] - d*R[n])
    return B, R

dt = 0.01
N = 6000
B, R = rovdyr_byttedyr(0.4, 0.02, 0.01, 0.3, dt, 40.0, 9.0, N)
for n in range(0, N+1, 1000):
    print(f't = {n*dt:5.1f}   B = {B[n]:7.2f}   R = {R[n]:6.2f}')

Utskrift:

t =   0.0   B =   40.00   R =   9.00
t =  10.0   B =   11.65   R =  25.91
t =  20.0   B =   47.80   R =   9.99
t =  30.0   B =   10.75   R =  22.17
t =  40.0   B =   55.70   R =  11.79
t =  50.0   B =   10.51   R =  18.95
t =  60.0   B =   62.39   R =  14.71

Tallene svinger: mange byttedyr gir god mat til rovdyrene, som formerer seg, som spiser ned byttedyrene, som gjør at rovdyrene sulter — og så begynner det på nytt. Byttedyrbestanden når toppen sin i år 3,3, år 22,5 og år 41,7, altså med litt over nitten år mellom hver topp, og rovdyrtoppene ligger drøyt tre år etter hver av dem.

At toppene vokser litt for hver runde (64,18, så 64,66, så 65,15) er ikke biologi, men numerikk: skrittmetoden legger på en liten feil hver runde. Halverer du Δt\Delta t, blir veksten mindre. Det er en god ting å nevne i en besvarelse, for det viser at du skiller modellen fra metoden.

60 år på 6000 skritt: legg merke til at N her ikke er antall år, men antall skritt, og at siste tidspunkt er N*dt =60= 60. Blander du de to, blir hele figuren feil skalert — uten at noe crasher.

Fasekurve

Et plott der den ene størrelsen står på førsteaksen og den andre på andreaksen, med tiden borte fra figuren: plt.plot(B, R). Hvert punkt på kurven er én tilstand systemet har vært i.

Lukker kurven seg til en løkke, gjentar systemet seg — det svinger. Spiraler den innover, dør svingningene ut; spiraler den utover, vokser de. Det er informasjon du ikke ser like lett i et tidsplott, og derfor spør oppgaven ofte om begge figurene.

Merk at det ikke skal være noen tidsakse i kallet: plt.plot(B, R) har begge tabellene som data. Skriver du plt.plot(t, B, R) i stedet, får du noe helt annet — det er et vanlig slurv.

✏️Eksempel 5: de to figurene av samme simulering

Plott bestandene fra eksempel 4 mot tid i samme figur med legende, og lag deretter fasekurven.


Først tidsplottet. Tidsaksen lages med linspace og har N+1 punkter, akkurat som B og R.

import numpy as np
import matplotlib.pyplot as plt

def rovdyr_byttedyr(a, b, c, d, dt, B0, R0, N):
    B = np.zeros(N+1)
    R = np.zeros(N+1)
    B[0] = B0
    R[0] = R0
    for n in range(N):
        B[n+1] = B[n] + dt*(a*B[n] - b*B[n]*R[n])
        R[n+1] = R[n] + dt*(c*B[n]*R[n] - d*R[n])
    return B, R

dt = 0.01
N = 6000
B, R = rovdyr_byttedyr(0.4, 0.02, 0.01, 0.3, dt, 40.0, 9.0, N)
t = np.linspace(0, N*dt, N+1)

plt.plot(t, B, label='byttedyr')
plt.plot(t, R, label='rovdyr')
plt.xlabel('tid (aar)')
plt.ylabel('antall (tusen)')
plt.legend()
plt.show()

Figur i ord: Førsteaksen er tid fra 0 til 60 år, andreaksen antall i tusen fra omtrent 8 til 65. To kurver svinger i takt, men forskjøvet: byttedyrkurven har topper rundt år 3, 22 og 42 og bunner ned mot 10 imellom, mens rovdyrkurven når sine topper drøyt tre år etter hver byttedyrtopp og går fra rundt 8 opp til nesten 40. Utslagene vokser så vidt merkbart gjennom figuren. Legenden øverst til høyre navngir de to kurvene.

Så fasekurven, som er den samme simuleringen tegnet uten tidsakse:

plt.plot(B, R)
plt.xlabel('byttedyr (tusen)')
plt.ylabel('rovdyr (tusen)')
plt.show()

Figur i ord: Førsteaksen er antall byttedyr fra 10 til 65, andreaksen antall rovdyr fra 8 til 40. Kurven går mot klokka i en løkke som ikke helt lukker seg — hver runde ender litt utenfor den forrige, så figuren blir en spiral som sakte utvider seg. Ingen legende, siden det bare er én kurve.

At spiralen utvider seg, er den samme numeriske effekten vi så i tallene. Den eksakte modellen gir en lukket løkke; skrittmetoden gir en som lekker litt.

📝Oppgave 6
Eksamensnivå, sjanger H
En kopp kaffe kjøles mot romtemperaturen 21 grader etter

Tn+1=TnkΔt(Tn21)T_{n+1} = T_n - k\,\Delta t\,(T_n - 21)

med k=0,06k = 0{,}06 og starttemperatur 92 grader. Den eksakte løsningen etter 30 minutter er 32,7362 grader.

a) Skriv avkjoling(T0, T_omg, k, dt, N).

b) Regn ut temperaturen etter 30 minutter med Δt=1\Delta t = 1; 0,50{,}5; 0,250{,}25 og 0,1250{,}125 minutt. Merk at NN må endres når Δt\Delta t endres, slik at sluttiden holder seg på 30 minutter.

c) Regn ut feilen mot det eksakte svaret og faktoren mellom radene. Hva sier faktoren om metoden?

📝Oppgave 7
Eksamensnivå, sjanger H…
To vannmagasiner er koblet med en tunnel. Volumene VV og WW (i millioner kubikkmeter) følger

Wn+1=Wn+Δt(0,3(VnWn)0,1Wn),Vn+1=Vn+Δt(20,3(VnWn))W_{n+1} = W_n + \Delta t\,(0{,}3(V_n - W_n) - 0{,}1W_n), \qquad V_{n+1} = V_n + \Delta t\,(2 - 0{,}3(V_n - W_n))

med Δt=0,25\Delta t = 0{,}25 døgn, V0=40V_0 = 40 og W0=8W_0 = 8.

a) Skriv magasiner(dt, V0, W0, N) som returnerer V, W.

b) Simulér 100 døgn og skriv ut volumene ved døgn 0, 25, 50 og 100.

c) Plott begge volumene mot tid i samme figur, med navngitte akser og legende. Beskriv figuren i ord.

📝Oppgave 8
Eksamensnivå, sjanger H…

Bruk rovdyr–byttedyr-modellen fra eksempel 4. Nå skal du undersøke hvor mye av svingningen som er modell og hvor mye som er metode.

a) Kjør simuleringen over de samme 60 årene for Δt=0,01\Delta t = 0{,}01; 0,0050{,}005; 0,00250{,}0025 og 0,001250{,}00125, og skriv ut sluttbestanden B(60)B(60) og den største byttedyrbestanden i hver kjøring. Husk at NN må endres når Δt\Delta t endres.

b) Hva viser kolonnene, og hva slags feil er det du ser krympe?

c) Plott fasekurven for den fineste steglengden, og beskriv den i ord.

API- og konstruksjonsliste

Begrepsbank

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

Sjanger H

Eksamensoppgaven «her er en regel for neste verdi; skriv koden som regner ut hele forløpet». Den er i 6 av 8 nyere sett (75 %) og i 4 av 4 siste, og veier 5 til 12 poeng. Oppgaven har nesten alltid en del a) med simuleringsfunksjonen og en del b) med plott.

Allokering

Å be om lagerplassen på forhånd, før du vet hva som skal stå der: x = np.zeros(N+1). Alternativet — å bygge en liste med append underveis — virker også, men tabellen er bokas form fordi den gjør antallet synlig og lar deg plotte direkte.

Skritt mot verdier
N skritt gir N+1 verdier, fordi startverdien også er en verdi. Løkka går over skrittene (range(N)), tabellen har plass til verdiene (np.zeros(N+1)). Bommer du på dette, er det feil #1 i feilkatalogen.
Startverdi

Verdien differenslikningen starter fra, satt med x[0] = x0 mellom allokeringen og løkka. Glemmer du den, starter forløpet på 0 uten noen feilmelding, fordi np.zeros allerede har lagt nuller i tabellen.

Koblet system

To differenslikninger der hver størrelse påvirker den andre. Løses med to tabeller, to startverdier og én løkke der begge oppdateres i samme runde. Returneres som return x, y.

Oppdateringsrekkefølge

Regelen om hvilken linje som skal stå først: inneholder høyresiden i den ene likningen en verdi med indeks n+1n+1, må linja som produserer den verdien stå foran. Å bomme her er feil #2 i feilkatalogen, og det gir tall uten feilmelding.

Stille feil

En feil som ikke gir feilmelding, bare gale tall. De to vanligste i dette kapitlet er glemt startverdi og feil oppdateringsrekkefølge — begge lever av at np.zeros har lagt en null der du leser. Motgiften er en kontrollregning.

Likevektsverdi

En verdi som gjentar seg selv i likningen: løsningen av x=F(x)x = F(x). Den kan regnes ut for hånd på et halvminutt og brukes til å kontrollere at simuleringen konvergerer mot riktig sted.

Tidsakse
t = np.linspace(0, N*dt, N+1) — like mange punkter som verditabellene, med sluttid N*dt. Uten den har figuren skrittnummer på førsteaksen i stedet for tid, som gir feil skala.
Fasekurve

Plottet plt.plot(x, y) der begge aksene er størrelser fra modellen og tiden ikke vises. En lukket løkke betyr at systemet gjentar seg; en spiral utover betyr voksende svingninger, ofte som følge av for stort tidssteg.

Steglengdekontroll

Kjør simuleringen på nytt med halvert Δt\Delta t og samme sluttid (altså dobbelt så mange skritt). Endrer svaret seg mye, er steget for stort. Endrer det seg lite, kan du stole på tallene. Dette er den vanligste kontrollen når det ikke finnes noe eksakt svar å sammenligne med.

Førsteordens metode

En metode der feilen halveres når steglengden halveres. Skrittmønsteret xn+1=xn+Δtf()x_{n+1} = x_n + \Delta t\,f(\ldots) er førsteordens, slik konvergenstabellen i oppgave 6 viser med faktor 2 i hver rad.

Repetisjonsoppgaver
Din fremgang
0 / 4 oppgaver

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 Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.