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**.
Dette kapitlet lærer deg sjanger H — differenslikninger, altså oppgaven «her er en regel for hvordan neste verdi følger av den forrige; skriv koden som regner ut hele forløpet». Du får utlevert en eller to likninger og noen startverdier, og skal levere en funksjon som fyller tabellene og gjerne plotter dem.
- Sjangeren er i 6 av de 8 nyere settene (75 %), og i 4 av 4 siste. Den har vært fast siden 2022.
- Vekten er 5 til 12 poeng — altså en av de tunge oppgavene på et sett med 75 poeng til sammen.
- Prioritet: kunne (nivå 2 av tre: må sitte, kunne, kjenne til). Med dagens trend grenser den til nivå 1.
Innpakningen roterer fra år til år — rovdyr og byttedyr, koblede vekstmodeller, en mekanisk modell — men programmeringsøvelsen er identisk hver gang: opprett tabellene, sett startverdiene, kjør en løkke, og pass på rekkefølgen når de to likningene er koblet. Det siste er det som skiller full uttelling fra halv, og det er derfor halve kapitlet handler om nettopp rekkefølgen.
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.]
42) 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 33) 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.0De 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.
Indeksen teller skritt, ikke tid direkte: er verdien du starter med, 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 og :
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]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.
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 med og . 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.44Les tallene: veksten er beskjeden i starten (7,68 millioner den første timen), er på sitt kraftigste midtveis, og bremser opp mot 500 — grensen . 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.
Dette er feil #1 i feilkatalogen — av-én-feilen i range og i antall punkter, den hyppigste feilen i hele faget. I differenslikninger har den to ansikter:
1. np.zeros(N) i stedet for np.zeros(N+1). Da har tabellen N plasser, mens løkka skriver til indeks N i siste runde. Programmet stopper med IndexError: index N is out of bounds for axis 0 with size N. Dette er den snille varianten, for den crasher.
2. for n in range(N+1) sammen med np.zeros(N+1). Da prøver siste runde å skrive til x[N+1], som heller ikke finnes — samme IndexError.
Husk regelen som ett bilde: skrittene er gjerdene mellom stolpene, verdiene er stolpene. N gjerder krever N+1 stolper. Løkka går over gjerdene: range(N).
Den farlige varianten er den som ikke crasher: skriver du np.zeros(N+1) og range(N-1), blir siste verdi stående som 0, og plottet ditt får en stygg loddrett strek ned til null helt til høyre. Ser du det i en figur, er det nesten alltid en av-én-feil.
(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)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.
En verdi som gjentar seg selv: setter du den inn i høyresiden, kommer den samme verdien ut igjen. For er likevekten løsningen av .
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 gir likevekten . 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 , altså 500, og tabellen sluttet på 497,44.
Behandlingen starter fra null: .
a) Skriv konsentrasjon(c0, N) som returnerer tabellen med de 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 på høyresiden i likningen for , 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.
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, yBlokken 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.
med , , og . 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 , 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.4802Massa 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.
Regelen for hvilken linje som skal stå først inne i løkka: den likningen hvis høyreside inneholder en verdi med indeks , 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 på hø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 -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.
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.5000Posisjonen 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.
1. Feil oppdateringsrekkefølge — dette er feil #2 i feilkatalogen: å regne x[n+1] før y[n+1] når x[n+1] trenger den nye y-verdien. Symptomet er som i eksempel 3: en av størrelsene står stille eller oppfører seg umulig. Bot: ring rundt -ledd på høyresiden før du skriver koden.
2. In-place-overskriving. Bruker du to enkle variabler i stedet for tabeller, og skriver
x = x + dt*y
y = y - dt*xså er x allerede endret når y oppdateres, og den gamle x-verdien er tapt. Med tabeller (x[n] og x[n+1]) skjer ikke dette, fordi den gamle verdien blir stående på sin egen plass. Det er én av grunnene til at boka alltid bruker tabeller. Blokken over er et fragment, ikke kjørbar kode.
3. Glemt startverdi for den ene av de to størrelsene. Da starter den på 0, og forløpet blir feil fra første skritt.
4. Rekursjon. Det frister å skrive en funksjon som kaller seg selv, siden likningen «henviser til seg selv». La være: rekursjon er utenfor pensum i dette emnet og markeres som uønsket i løsningsforslagene. Alle differenslikninger i faget løses med løkke som lagrer verdiene i en tabell — også de som bruker to gamle verdier.
(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)der er den årlige snøtilførselen. Bruk , , , og .
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 og skriv ut verdiene for og . Hva nærmer de seg?
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.
Skrittene svarer til tidspunktene . 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]
6Merk 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.
med , , , , , og . Simulér 60 år og vis bestandene hvert tiende år.
Her bruker ingen av høyresidene en verdi med indeks : begge er uttrykt i og 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.71Tallene 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 , 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 . Blander du de to, blir hele figuren feil skalert — uten at noe crasher.
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.
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.
med 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 ; ; og minutt. Merk at må endres når 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?
med døgn, og .
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.
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 ; ; og , og skriv ut sluttbestanden og den største byttedyrbestanden i hver kjøring. Husk at må endres når 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.
1. #1 av-én. np.zeros(N+1) og range(N). N skritt gir N+1 verdier. Feil her gir enten IndexError eller en siste verdi som blir stående på 0.
2. #2 feil oppdateringsrekkefølge. Bruker den ene høyresiden en verdi med indeks , skal den linja stå sist. Symptomet er en størrelse som står stille eller vokser umulig.
3. Glemt startverdi. Ingen feilmelding — forløpet starter bare på 0.
4. N forvekslet med sluttid. Sluttiden er N*dt. Skriver du np.linspace(0, N, N+1) når stegene er dt, blir hele førsteaksen feil skalert.
5. Tidsakse med feil lengde. t må ha nøyaktig N+1 punkter, ellers klager plottefunksjonen på at tabellene ikke har samme lengde.
6. Glemt plt.legend(). label= alene viser ingen legende. Begge deler kreves, og manglende legende trekker i to kurver i samme figur.
7. In-place-overskriving med enkle variabler i stedet for tabeller, slik at den gamle verdien er tapt når den andre likningen skal bruke den.
8. Rekursjon. Utenfor pensum i dette emnet — differenslikninger løses med løkke.
9. Ingen kontroll. Regn ut likevekten for hånd, eller halvér steget og se om svaret nærmer seg noe. En simulering uten kontroll er et tall uten dekning.
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
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.
Å 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.
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.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.
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.
Regelen om hvilken linje som skal stå først: inneholder høyresiden i den ene likningen en verdi med indeks , må linja som produserer den verdien stå foran. Å bomme her er feil #2 i feilkatalogen, og det gir tall uten feilmelding.
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.
En verdi som gjentar seg selv i likningen: løsningen av . Den kan regnes ut for hånd på et halvminutt og brukes til å kontrollere at simuleringen konvergerer mot riktig sted.
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.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.
Kjør simuleringen på nytt med halvert 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.
En metode der feilen halveres når steglengden halveres. Skrittmønsteret er førsteordens, slik konvergenstabellen i oppgave 6 viser med faktor 2 i hver rad.
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.