Tilbake
3.3

3.3 DRILL — Rekke-implementering med testfunksjon

Full drill på F-sjangeren: fra rekkeformel til kompakt funksjon + testfunksjon, med roterende innpakning.

75 min
12 oppgaver
DRILLRekke-implementering med testfunksjon
Din fremgang i kapitlet
0 / 12 oppgaver

Forkunnskaper — sist du var her

Drillen forutsetter kap. 3.1 (testfunksjoner) og kap. 3.2 (rekkemønsteret). Her er de tre grepene du kommer til å bruke i hver eneste oppgave, oppfrisket med kjørt utskrift.

1. Summeringsmønsteret. Nullstill, løkk, legg til, returnér — i den rekkefølgen.

from math import factorial

def exp_rekke(x, N):
    s = 0
    for k in range(N+1):
        s += x**k / factorial(k)
    return s

print(exp_rekke(1.0, 10))

Utskrift:

2.7182818011463845

2. Testfunksjonen. Ingen argumenter, ingen utskrift, alltid toleranse.

from math import factorial, exp

def exp_rekke(x, N):
    s = 0
    for k in range(N+1):
        s += x**k / factorial(k)
    return s

def test_exp_rekke():
    x = 0.5
    expected = exp(x)
    computed = exp_rekke(x, 15)
    tol = 1e-10
    msg = f'ventet {expected}, fikk {computed}'
    assert abs(expected - computed) < tol, msg

test_exp_rekke()
print('Testen gikk gjennom')

Utskrift:

Testen gikk gjennom

3. Rekurrensen med to lagervariabler. Regn den nye verdien først, flytt lagrene etterpå.

def chebyshev(n, x):
    if n == 0:
        return 1.0
    T_forrige = 1.0
    T_denne = x
    for k in range(1, n):
        T_neste = 2*x*T_denne - T_forrige
        T_forrige = T_denne
        T_denne = T_neste
    return T_denne

print(chebyshev(3, 0.5), chebyshev(4, 0.5))

Utskrift:

-1.0 -0.5

Er noe av dette uklart, les de to forrige kapitlene først — drillen forklarer ikke konstruksjonene på nytt.

📜Løsningsoppskriften for sjanger F — sju steg

Les oppgaveteksten, og gjør så dette, i rekkefølge. Hvert steg svarer til ett spørsmål du besvarer ved å se på formelen som står i oppgaven.

1. Skriv ned ledd-uttrykket. Alt til høyre for summetegnet, oversatt tegn for tegn til Python. Ikke forenkle. x**k / factorial(k), (-1)**k * x**(2*k) / factorial(2*k).
2. Les startverdien under summetegnet. Er den 0, er løkka range(...); er den 1, er den range(1, ...).
3. Les sluttverdien over summetegnet. «Til og med NN» gir range(N+1). «De første NN leddene» gir range(N). Er teksten tvetydig, velg og skriv én kommentarlinje.
4. Skriv de fire linjene: s = 0, løkka, s += ledd, return s.
5. Skriv testfunksjonen: ingen argumenter, expected fra en uavhengig kilde, computed fra funksjonen din, tol valgt etter metoden, og assert abs(expected - computed) < tol, msg.
6. Sjekk indekseringen på oddetalls- og partallsledd: står 2*n+1 i eksponenten, skal det stå i nevneren også.
7. Er rekka rekurrent? Da bytter du mal: basistilfelle med if, to lagervariabler, hjelpevariabel for den nye verdien, løkke fra 1 til nn.

Skjelettet er alltid dette, og det er verdt å skrive ned først, før du fyller inn ledd-uttrykket:

def rekke(x, N):
    s = 0
    for k in range(N+1):
        s += 0.0
    return s

print(rekke(2.0, 5))

Utskrift:

0.0

Skjelettet gir uttelling i seg selv. Fyller du inn feil ledd, mister du ett eller to poeng — glemmer du skjelettet, mister du oppgaven.

Det gjennomarbeidede caset (~20 min)

Under står en komplett sjanger-F-oppgave av samme type og vekt som på et virkelig sett, med sensorkommentar ved hvert steg. Les den som en modell: det er nøyaktig denne formen svarene dine skal ha.

Oppgaven (6 poeng). Når en lysstråle går fra luft til vann, gir brytningsloven at utgangsvinkelen er θ=arcsin(x)\theta = \arcsin(x), der xx er sinus til innfallsvinkelen delt på brytningsindeksen. En innebygd arcsin er ikke alltid tilgjengelig i innvevd programvare, og da brukes rekka

arcsinx=n=0(2n)!4n(n!)2(2n+1)x2n+1,x<1\arcsin x = \sum_{n=0}^{\infty} \frac{(2n)!}{4^n (n!)^2 (2n+1)} x^{2n+1}, \qquad |x| < 1

a) (4 p) Skriv arcsin_rekke(x, N) som summerer leddene fra n=0n = 0 til og med n=Nn = N.

b) (2 p) Skriv en testfunksjon som sammenligner med math.asin for x=0,4x = 0{,}4.

✏️Modellbesvarelse del (a): fra formel til fire linjer

Del (a), 4 poeng: arcsin_rekke(x, N) skal summere leddene til og med n=Nn = N.

Gå gjennom oppskriften. Ledd-uttrykket har fakultet i både teller og nevner, en firerpotens og en oddetallspotens av xx. Startverdien er 0, sluttverdien er NN og «til og med», så løkka er range(N+1).

Skriv ledd-uttrykket som det står. (2n)! blir factorial(2*n), 4n4^n blir 4**n, (n!)2(n!)^2 blir factorial(n)**2, og 2n+12n+1 blir (2*n+1).

from math import factorial

def arcsin_rekke(x, N):
    s = 0
    for n in range(N+1):
        s += factorial(2*n) * x**(2*n+1) / (4**n * factorial(n)**2 * (2*n+1))
    return s

for N in [0, 2, 5, 20]:
    print(N, arcsin_rekke(0.4, N))

Utskrift:

0 0.4
2 0.41143466666666667
5 0.4115167123246176
20 0.41151684606748806

Sensorkommentar, steg for steg:

- s = 0 før løkka — 0,5 p. En ren rutinelinje, men den gis poeng, og den kan skrives før du har forstått formelen.
- for n in range(N+1):1 p. Dette er stedet av-én-feilen slår til. range(N) gir samme kode med ett ledd mindre.
- Ledd-uttrykket — 2 p. Delt i to: 1 poeng for oddetallspotensen x**(2*n+1), 1 poeng for nevneren med alle tre faktorene. Bommer du på én faktor i nevneren, får du 1 av 2.
- return s utenfor løkka — 0,5 p. Står return inne i løkka, mister du dette og strukturpoenget over.

Hva som er godt nok. Skriver du nevneren som 4**n * factorial(n)*factorial(n) * (2*n+1), er det like riktig. Deler du ledd-uttrykket i to linjer med en hjelpevariabel ledd, er det også like riktig — og litt lettere å lese:

from math import factorial

def arcsin_rekke(x, N):
    s = 0
    for n in range(N+1):
        teller = factorial(2*n) * x**(2*n+1)
        nevner = 4**n * factorial(n)**2 * (2*n+1)
        s += teller / nevner
    return s

print(arcsin_rekke(0.4, 20))

Utskrift:

0.41151684606748806

Kontrollen du kan gjøre uten datamaskin: med N=0N = 0 er leddet xx selv, siden 0!=10! = 1, 40=14^0 = 1 og 20+1=12\cdot 0 + 1 = 1. Utskriften bekrefter det — 0,4 på første linje. Gir din funksjon noe annet, er indekseringen gal, og det oppdager du på ti sekunder.

✏️Modellbesvarelse del (b): testfunksjonen, og hvor mange ledd som trengs

Del (b), 2 poeng: skriv en testfunksjon mot math.asin for x=0,4x = 0{,}4. Hvor mange ledd må du ta med for at tol = 1e-10 skal holde?

Fasiten hentes fra math.asin, som regner ut arcus sinus på en helt annen måte enn rekka — det er nettopp det som gjør den til en uavhengig kilde.

from math import factorial, asin

def arcsin_rekke(x, N):
    s = 0
    for n in range(N+1):
        s += factorial(2*n) * x**(2*n+1) / (4**n * factorial(n)**2 * (2*n+1))
    return s

def test_arcsin_rekke():
    x = 0.4
    expected = asin(x)
    computed = arcsin_rekke(x, 20)
    tol = 1e-10
    msg = f'ventet {expected}, fikk {computed}'
    assert abs(expected - computed) < tol, msg

test_arcsin_rekke()
print('Testen gikk gjennom')
print(asin(0.4))

Utskrift:

Testen gikk gjennom
0.41151684606748806

Fra tabellen i del (a) ser du hvor mange ledd som trengs: med tre ledd (N=2N = 2) stemmer tre desimaler, med seks ledd stemmer seks, og med 21 ledd er alle sifrene like. Toleransen 1e-10 krever altså et sted mellom ti og tjue ledd, og N=20N = 20 er et trygt valg.

Sensorkommentar del (b):

- Testfunksjon uten argumenter, med navn test_ pluss det som testes — 0,5 p.
- expected fra math.asin, altså en uavhengig kilde — 0,5 p.
- assert abs(expected - computed) < tol med melding — 1 p. Det er her feil #11, likhetstegn på flyttall, koster hele poenget.

Om verdiområdet. Rekka konvergerer bare for x<1|x| < 1, og saktere jo nærmere 1 du kommer. Prøver du x=0,7x = 0{,}7 med elleve ledd, treffer du bare fem desimaler:

from math import factorial, asin

def arcsin_rekke(x, N):
    s = 0
    for n in range(N+1):
        s += factorial(2*n) * x**(2*n+1) / (4**n * factorial(n)**2 * (2*n+1))
    return s

for x in [0.1, 0.3, 0.5, 0.7]:
    print(f'{x:4.1f} {arcsin_rekke(x, 10):10.6f} {asin(x):10.6f}')

Utskrift:

 0.1   0.100167   0.100167
 0.3   0.304693   0.304693
 0.5   0.523599   0.523599
 0.7   0.775394   0.775397

Å nevne dette i én setning i besvarelsen — «testpunktet er valgt godt innenfor konvergensområdet» — er den typen presisjon som skiller en god besvarelse fra en riktig en. Det er ikke et krav for full score.

Merk hva som ikke kreves: ingen brukerinput, ingen utskrift fra funksjonene, ingen kommentarer utover eventuelle antakelser. Ni linjer kode for seks poeng.

Oppgaver: samme mønster, ny innpakning (~30 min for oppgave 1–6)

— naturlig pausepunkt —

Alle oppgavene under er på eksamensnivå. Innpakningen roterer bevisst, og både startverdi, sluttverdi og leddform varierer. Bruk oppskriftens sju steg på hver oppgave før du skriver en linje kode.

📝Oppgave 1

(Midtveisnivå, sjanger A — kodesporing, altså «hva skriver dette programmet ut?».) Hva skriver programmet ut? Skriv utskriften nøyaktig, med mellomrom og linjeskift slik terminalen viser den.

s = 0
for k in range(1, 4):
    s += (-1)**k * k
    print(k, s, end='  ')
print()

📝Oppgave 2
Eksamensnivå, sjanger F
Aktiviteten i en radioaktiv prøve avtar som A(t)=A0eλtA(t) = A_0 e^{-\lambda t}. Et måleinstrument uten innebygd eksponentialfunksjon bruker rekka

ey=k=0Nykk!e^{y} = \sum_{k=0}^{N} \frac{y^k}{k!}

med y=λty = -\lambda t.

a) Skriv exp_rekke(y, N).

b) Skriv en testfunksjon for y=0,7y = -0{,}7 med toleranse 1e-10, og velg NN selv.

c) Hvorfor er det uproblematisk at yy er negativ her, selv om alle leddene i formelen ser positive ut?

📝Oppgave 3
Eksamensnivå, sjanger F
Den logaritmiske veksten i et biologisk mål beskrives med ln(1+x)\ln(1+x), som har rekka

ln(1+x)=k=1N(1)k+1xkk\ln(1+x) = \sum_{k=1}^{N} (-1)^{k+1}\frac{x^k}{k}

a) Skriv ln1p_rekke(x, N).

b) Vis at rekka konvergerer langsomt ved å regne den ut for N=3N = 3, 1010 og 4040 med x=0,2x = 0{,}2, og sammenlign med math.log(1.2).

c) Hvilken toleranse ville du valgt i en testfunksjon med N=10N = 10?

📝Oppgave 4
Eksamensnivå, sjanger F
Formen på en hengende kjede mellom to master beskrives av cosh\cosh, som har rekka

coshx=n=0Nx2n(2n)!\cosh x = \sum_{n=0}^{N} \frac{x^{2n}}{(2n)!}

a) Skriv cosh_rekke(x, N).

b) Skriv en testfunksjon mot math.cosh for x=1,4x = 1{,}4.

c) Hva gir funksjonen for N=0N = 0, og hvorfor er det en god kontroll?

📝Oppgave 5
Eksamensnivå, sjanger F
En fallskjermhopper når terminalhastigheten vTv_T, og tiden det tar å komme opp i en brøkdel xx av den kan skrives med artanhx\operatorname{artanh} x. Rekka er

artanhx=n=0Nx2n+12n+1,x<1\operatorname{artanh} x = \sum_{n=0}^{N} \frac{x^{2n+1}}{2n+1}, \qquad |x| < 1

a) Skriv atanh_rekke(x, N).

b) Skriv en testfunksjon for x=0,6x = 0{,}6 som faktisk går gjennom. Du må velge NN og tol som henger sammen, og begrunne valget i en kommentar.

c) Forklar hvorfor tol = 1e-10 med N=10N = 10 ville feilet, uten at koden er gal.

📝Oppgave 6
Eksamensnivå, sjanger F
Chebyshev-polynomene brukes til å fordele tilnærmingsfeilen jevnt over et intervall, og er definert ved

T0(x)=1,T1(x)=x,Tn+1(x)=2xTn(x)Tn1(x)T_0(x) = 1, \qquad T_1(x) = x, \qquad T_{n+1}(x) = 2xT_n(x) - T_{n-1}(x)

a) Skriv chebyshev(n, x) med en løkke.

b) Skriv en testfunksjon som sjekker mot de kjente formlene T2(x)=2x21T_2(x) = 2x^2 - 1 og T3(x)=4x33xT_3(x) = 4x^3 - 3x for x=0,3x = 0{,}3.

c) Hvorfor må den nye verdien regnes ut i en egen variabel før lagrene flyttes?

Oppgave 7–12: variantene som skiller (~25 min)

— naturlig pausepunkt —

De neste seks oppgavene ligner mest på det du møter når sjangeren er verdt seks poeng i stedet for fire: en «finn feilen», en «hvilken linje mangler», en rekke som styres av en toleranse i stedet for et antall ledd, og en rekurrens med en formel som varierer med leddnummeret.

📝Oppgave 7
Eksamensnivå, sjanger O

Programmet skal summere n=0Nx2n+1/(2n+1)!\sum_{n=0}^{N} x^{2n+1}/(2n+1)! og teste mot math.sinh. Det har fire feil.

import sinh from math
from math import factorial

def sinh_rekke(x, N):
    s = 0
    for n in range(N):
        s += x**(2*n+1) / factorial(n)
        return s

def test_sinh_rekke(x, N):
    assert sinh_rekke(x, N) == sinh(x)

a) Nevn de fire feilene og si hva hver av dem fører til.

b) Skriv den rettede koden.

📝Oppgave 8

(Midtveisnivå, sjanger E — «hvilken linje mangler?». Sjanger E er flervalgstypen der ett kall eller én linje er fjernet fra et ellers riktig program.) Funksjonen skal summere k=0Nxk/k!\sum_{k=0}^{N} x^k/k!, men én linje er fjernet.

from math import factorial

def exp_rekke(x, N):
    # HER MANGLER ÉN LINJE
    for k in range(N+1):
        s += x**k / factorial(k)
    return s

Hvilken linje hører på plassen?

a) s = 1
b) s = 0
c) s = x
d) return s

📝Oppgave 9
Eksamensnivå, sjanger F

I stedet for å oppgi antall ledd, ber oppgaven om at summeringen skal stoppe når leddet er blitt lite nok.

a) Skriv exp_tol(x, eps) som summerer rekka xk/k!\sum x^k/k! og stopper når absoluttverdien av det siste leddet er mindre enn eps. Funksjonen skal returnere både summen og antall ledd som ble brukt.

b) Kjør den for x=1,5x = 1{,}5 med eps lik 1e-4 og 1e-10, og sammenlign med math.exp.

c) Hvorfor må ledd settes til en verdi større enn eps før løkka?

📝Oppgave 10
Eksamensnivå, sjanger F
Legendre-polynomene brukes i modeller for tyngdefeltet rundt en ujevn planet, og oppfyller

P0(x)=1,P1(x)=x,Pn+1(x)=(2n+1)xPn(x)nPn1(x)n+1P_0(x) = 1, \qquad P_1(x) = x, \qquad P_{n+1}(x) = \frac{(2n+1)xP_n(x) - nP_{n-1}(x)}{n+1}

Legg merke til at koeffisientene her avhenger av nn, i motsetning til i Chebyshev-formelen.

a) Skriv legendre(n, x) med en løkke.

b) Skriv en testfunksjon mot de kjente formlene P3(x)=(5x33x)/2P_3(x) = (5x^3 - 3x)/2 og P4(x)=(35x430x2+3)/8P_4(x) = (35x^4 - 30x^2 + 3)/8 for x=0,6x = 0{,}6.

c) Hva må du passe ekstra på når koeffisientene avhenger av leddnummeret?

📝Oppgave 11

(Midtveisnivå, sjanger D — «hvilke av påstandene er riktige?». Sjanger D er matche-typen der du avgjør om hver påstand holder.) Funksjonen under skal summere k=0Nxk/k!\sum_{k=0}^{N} x^k/k!.

def rekke(x, N):
    s = 0
    for k in range(N+1):
        s += x**k / factorial(k)
    return s

Avgjør for hver påstand om den er riktig eller gal.

RadPåstand
1Løkka går N+1 runder
2s = 0 kan like gjerne stå inne i løkka
3return s må stå utenfor løkka
4Funksjonen krever from math import factorial for å virke
5range(N+1) gir verdiene 1 til og med N+1
6Kalt med N = 0 returnerer funksjonen 1.0

📝Oppgave 12
Eksamensnivå, sjanger F
Sinus har rekka

sinx=n=0N(1)nx2n+1(2n+1)!\sin x = \sum_{n=0}^{N} (-1)^n \frac{x^{2n+1}}{(2n+1)!}

a) Skriv sin_rekke(x, N).

b) Skriv en testfunksjon som sjekker to punkter: ett der du kjenner svaret uten å regne, og ett der fasiten hentes fra math.sin.

c) Skriv koden som lager en tabell over xx, sin_rekke(x, 5) og math.sin(x) for fem verdier av xx mellom 0 og 3, formatert med seks desimaler.

d) Hva ser du i tabellen når xx vokser, og hvorfor?

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 F

Bokas kode for oppgaven «skriv en funksjon som summerer en rekke, og skriv en testfunksjon til den». Sjangeren står i 8 av 8 gjennomgåtte avsluttende sett og teller 4–6 poeng, delt i del-a (summen) og del-b (testen). Innpakningen roterer år for år; øvelsen er identisk.

Steg 1 — skriv ledd-uttrykket som det står

Alt til høyre for summetegnet oversettes tegn for tegn til Python, uten forenkling. (2n)! blir factorial(2*n), 4n4^n blir 4**n, (n!)2(n!)^2 blir factorial(n)**2. Forenkling underveis er en av de vanligste kildene til feil under tidspress.

Steg 2 og 3 — les grensene av summetegnet

Startverdien står under tegnet og blir første argument i range; sluttverdien står over og blir stoppverdien pluss én, fordi range er eksklusiv i den enden. «De første NN leddene» er den ene formuleringen som gir range(N).

Steg 4 — de fire linjene
s = 0, løkka, s += ledd, return s. Rekkefølgen og innrykkene er hele strukturpoenget, og skjelettet er verdt å skrive ned før du har forstått formelen.
Steg 5 — testfunksjonen

Ingen argumenter, expected fra en uavhengig kilde, computed fra funksjonen, tol valgt etter metoden, og assert abs(expected - computed) < tol, msg. Testfunksjonen kan skrives selv om del-a ikke ble ferdig — du vet hva funksjonen skal gjøre.

Steg 6 — indekseringskontrollen

Står 2*n+1 i eksponenten, skal factorial(2*n+1) stå i nevneren. Ulik indeksering gir en helt annen funksjon, og kontrollen tar to sekunder.

Steg 7 — den rekurrente malen

Basistilfelle med if øverst, to lagervariabler satt til startverdiene, hjelpevariabel for den nye verdien, og løkke fra 1 til nn. Merk at rekurrensen ikke har noen akkumulator i det hele tatt — det er en annen løkke enn summeringsmønsteret.

Uavhengig fasit
expected skal komme fra en annen vei enn koden: math, en kjent eksplisitt formel som T3(x)=4x33xT_3(x) = 4x^3 - 3x, eller håndregning. Gjentar fasiten koden, tester du ingenting.
`eps` mot `tol`

To forskjellige ting med lignende navn. eps er stoppgrensen i en while-variant og styrer hvor mange ledd som tas med. tol er toleransen i testfunksjonen og styrer hvor stort avvik testen godtar. En oppgave kan bruke begge samtidig.

`while`-varianten

Når oppgaven sier «summer til leddet er mindre enn», er antall runder ukjent, og while abs(ledd) > eps: er riktig løkke. Leddnummeret telles opp for hånd med k += 1, og startverdien på ledd må være større enn eps så løkka i det minste får gå én gang.

Terminering av en rekkesum

En while-løkke over rekkeledd stopper fordi leddene går mot null. For xk/k!x^k/k! vinner fakultetet til slutt, uansett hvor stor xx er. Én setning om at løkka terminerer er verdt å ta med når oppgaven bruker while.

Gratiskontrollen «hva gir N = 0?»

Første ledd i eksponentialrekka er 1, i partallsrekkene 1, og i oddetallsrekkene xx selv. Kontrollen tar ti sekunder, krever ingen datamaskin, og avslører de fleste indekseringsfeil.

Konvergensområde

Noen rekker gjelder bare for visse xx: arcus sinus og invers tangens hyperbolicus krever x<1|x| < 1, og konvergerer saktere jo nærmere 1 du kommer. Eksponential-, sinus- og cosinusrekkene konvergerer for alle xx, men trenger flere ledd når xx er stor.

Delpoeng i sjanger F

Sensor gir uttelling steg for steg: akkumulator, løkkegrense, ledd-uttrykk, return, testfunksjon. Riktig struktur med ett feil ledd gir de fleste poengene; riktig ledd uten struktur gir få. Skriv derfor skjelettet først.

Repetisjon: oppskriften på ett kort

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.