Skjermfilmer, funksjoner

Tabellen under gir en oversikt over alle skjermfilmer det refereres til på nettstedet under menyvalget Funksjoner.

Artikkel Beskrivelse og film
Nullpunkter og ekstremalpunkter

Vi bruker skjæringssetningen til å undersøke om likningen x5 + x4 + x3 + x − 1 = 0 har en løsning i intervallet [−1, 1].
skjaeringssetningen

Vi klassifiserer fire ekstremalpunkter på en graf.
ekstremalpunkter

Kontinuitet og grenser

Vi undersøker om f(x) = |x| er kontinuerlig i x = 0.
kontinuitet

Vi illustrerer epsilon-delta definisjonen for grenseverdi.
grenseverdi

Asymptoter

Vi finner horisontale og vertikale asymptoter til funksjonen $\frac{\displaystyle -x^2 + x – 2}{\displaystyle x^2 – 1}$.
asymptoter

Trigonometriske funksjoner

Vi illustrerer hva som menes med en periodisk funksjon.
periodisitet

Vi regner med trigonometriske og inverse trigonometriske funksjoner, vinkler både i grader og radianer.
trigonometri

Ubestemte integraler

Vi ser eksempler på integrasjon av potensfunksjoner.
Integrasjon av potensfunksjoner

Vi ser eksempler på integrasjon av potensfunksjoner, trigonometrisk funksjoner og eksponentialfunksjoner.
Integrasjon av diverse funksjoner

Bestemte integraler

Vi beregner et bestemt integral til en polynomfunksjon.
Bestemt integral

Integral som areal

Vi ser et eksempel på hvordan integrasjon kan brukes til å finne arealet under en graf.
Areal under en graf

Integrasjon ved substitusjon

Vi ser et eksempel på integrasjon ved substitusjon.
Integrasjon ved substitusjon

Delvis integrasjon

Vi ser et eksempel på delvis integrasjon.
Delvis integrasjon

Vi ser et eksempel på delvis integrasjon der det opprinnelige integralet dukker opp igjen underveis.
Delvis integrasjon med gjentatt ledd

Fordeler og ulemper med rekursjon

En stor fordel med rekursjon er at vi kan kode en funksjonsdefinisjon så å si direkte, uten å behøve å tenke på løkker og løkkevariable. Rekursjon gir ofte enkle og elegante løsninger. Sammenlikn for eksempel den iterative og rekursive varianten av fakultetsfunksjonen vist i eksempel 1:

Eksempel 1:

Iterativ versjon:

def fakultet(n):
    """Beregner n! ved iterasjon"""
    if n == 0:
        return 1
    else:
        produkt = 1
        for m in range(1, n + 1):
            produkt *= m
        return produkt

Rekursiv versjon:

def fakultet(n):
    """Beregner n! ved rekursjon"""
    if n == 0:
        return 1
    else:
        return n * fakultet(n - 1)

Av og til kan det faktisk være vanskelig å finne en løsning som ikke er rekursiv. Ulempen med rekursjon er at det er atskillig mindre effektivt enn løkker, fordi å håndtere funksjonskall krever mye ekstra arbeid, såkalt overhead, av datamaskinen. I praksis vil det også finnes en grense for hvor mange funksjonskall vi kan kjede etter hverandre. Går vi over grensa, får vi en feilmelding. I Python ligger denne grensa på rundt 1000.

Av og til kan rekursjon også være en håpløst ineffektiv løsning. Vi skal illustrere dette med en funksjon for å beregne fibonaccitall. Fibonaccitallene er 1, 1, 2, 3, 5, 8, …, en tallfølge der de to første tallene er 1, og hvert tall deretter er lik summen av de to foregående.

En iterativ versjon av en funksjon for å beregne fibonaccitall er vist i eksempel 2.

Eksempel 2:

def fibonacci(n):
    """Beregner fibonaccitall nummer n ved iterasjon."""
    pre_fibo = 1
    fibo = 1
    for _ in range(n - 2):
        fibo, pre_fibo = fibo + pre_fibo, fibo
    return fibo

Hvis vi har funksjonen i eksempel 2, og i hovedprogrammet skriver

for n in range(1, 101):
    print(f"Fibonacctitall {n} er {fibonacci(n)}")

skrives de 100 første fibonaccitallene kjapt ut.

I eksempel 2 representerer fibo fibonaccitallet og pre_fibo tallet før fibonaccitallet. Disse settes i utgangspunktet til 1, siden det første og andre fibonaccitallet er 1. Hvis funksjonen kalles opp med n lik 1 eller 2, gjennomløpes ikke løkka, og fibo beholder verdien 1. Hvis n er 3 eller større, gjennomløpes løkka, der fibo blir satt lik fibo + pre_fibo og pre_fibo blir satt lik fibo.

Det krever imidlertid en del tankearbeid å forstå logikken i eksempel 2. Den rekursive varianten i eksempel 3 er mye enklere å forstå.

Eksempel 3:

def fibonacci(n):
    """Beregner fibonaccitall nummer n ved rekursjon."""
    if n <= 2:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

I eksempel 3 benytter vi definisjonen av fibonaccitall direkte. Hvis funksjonen kalles opp med n lik 1 eller 2, er returverdien 1. Hvis n er 3 eller større, er returverdien summen av de to foregående fibonaccitallene.

Men hvis vi nå i hovedprogrammet igjen skriver

for n in range(1, 101):
    print(f"Fibonacctitall {n} er {fibonacci(n)}")

ser vi at det er noe som ikke fungerer. Tallene skrives ut langsommere og langsommere, og i nærheten av fibonaccitall 40 stopper det nesten helt opp. Sjekk gjerne ut dette selv.

Grunnen er at vi har to rekursive kall i funksjonen, nemlig fibonacci(n – 1) og fibonacci(n – 2), slik at vi for hvert rekursjonskall genererer to nye, og vi får en eksponentiell vekst i kompleksitet. Samme fibonaccitall beregnes om og om igjen. Trestrukturen under viser hvordan funksjonskallene skjer når vi skal beregne fibonaccitall nummer 6:

Rekursjonstre for fibonaccitall

Vi ser at fibonaccitall nummer 2 beregnes hele 5 ganger.

Rekursjon bør vi generelt ikke bruke hvis et rekursivt kall genererer mer enn ett nytt.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Rekursjon

I artikkelen om å lagre funksjoner i moduler finner  vi funksjonen fakultet(), som beregner fakultetet til et tall, n!

def fakultet(n):
    """Beregner n!"""
    if n == 0:
        return 1
    else:
        produkt = 1
        for m in range(1, n + 1):
            produkt *= m
        return produkt

Funksjonen returnerer 1 hvis n er 0, ellers bruker den ei løkke som multipliserer tallene fra 1 til n. Nå skal vi skrive den på en annen måte.

Hvis skal finne fakultetet til et tall, n!, og allerede kjenner fakultetet til det forrige tallet, (n − 1)!, trenger vi jo ikke starte på 1 og multiplisere oss oppover, alt vi behøver gjøre er å multiplisere det nye tallet med fakultetet vi kjenner: n! = n · (n − 1)! Hvis vi for eksempel vet at 4! = 24, blir 5! = 5 · 4! = 5 · 24 = 120.

Dette mønsteret gjelder for alle n > 0, for n = 0 er det definert at 0! = 1. Vi kan angi det slik:

$n!\, =\, \left\{ {\begin{array}{l}
1\, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \,
n\, =\, 0 \\
\, \\
n\cdot \left( n-1 \right)!\, \, \, \, \, n>0\, \\
\end{array}} \right.$

Så spør vi oss om det er mulig å programmerere dette mønsteret direkte i en funksjon, slik:

def fakultet(n):
    """Beregner n!"""
    if n == 0:
        return 1
    else:
        return n * fakultet(n - 1)

Svaret er ja. I artikkelen om funksjoner som kaller funksjoner ser vi at funksjoner kan kalle andre funksjoner, her ser vi at funksjoner også kan kalle seg selv. Dette kalles rekursjon.

Vi har nå skrevet funksjonen fakultet() på to måter. Én der beregningen foregår ved hjelp av ei løkke, noe som kalles en iterativ metode, og én der beregningen foregår ved at funksjonen kaller seg selv, noe vi kaller en rekursiv metode.

Rekursjon fungerer fordi variable definert inni en funksjon er lokale i funksjonen, slik vi beskriver i artikkelen om lokale og globale variabler. Hadde variabelen n i fakultet() vært global, ville rekursjon ikke fungert.

La oss som en illustrasjon tenke oss at vi i hovedprogrammet skriver kode som kaller opp fakultet() med argument 2. Siden vi kommer til å ha flere utgaver av n og fakultet(), bruker vi fargekoder for å holde dem fra hverandre.

Vi skriver altså først:

print(fakultet(2))

Da blir verdien 2 overført til parameteren n i fakultet().

Siden n ikke er 0, blir kodeblokka tilhørende else utført:

return n * fakultet(n-1)

Her kaller fakultet() opp seg selv med n − 1 = 2 − 1 = 1 som argument. Da blir verdien 1 overført til parameteren n i fakultet().

Siden n ikke er 0, blir kodeblokka tilhørende else utført:

return n * fakultet(n-1)

Her kaller fakultet() opp seg selv med n − 1 = 1 − 1 = 0 som argument. Da blir verdien 0 overført til parameteren n i fakultet().

(Vi har nå tre utgaver av fakultet() aktive, med henholdsvis n = 2, n = 1 og n = 0.)

Siden n er 0, blir kodeblokka tilhørende if utført:

return 1

Denne returverdien går tilbake til der fakultet() ble kalt opp:

return n * fakultet(n-1)

Her er n = 1 og returverdien fra kallet fakultet(n-1) er 1, så dette blir 1 · 1 = 1.

Denne returverdien går tilbake til der fakultet() ble kalt opp:

return n * fakultet(n-1)

Her er n = 2 og returverdien fra kallet fakultet(n-1) er 1, så dette blir 2 · 1 = 2.

Denne returverdien går tilbake til der fakultet() ble kalt opp:

print(fakultet(2))

Og vi får skrevet ut 2.

I et rekursivt system genereres en kjede av funksjonskall. Men det må være en grunnverdi der det ikke lenger genereres nye kall. I fakultetsfunksjonen er det n = 0, der vi vet at funksjonen skal returnere 1 og ikke trenger flere kall. Mangler vi en grunnverdi, får vi teoretisk en rekursjonskjede uten ende. I praksis vil Python etter om lag 1000 rekursive kall gi feilmeldingen "RecursionError: maximum recursion depth exceeded".

Python Tutor, er et supert verktøy til å utforske rekursjon. I bildet under har vi lagt inn den rekursive funksjonen fakultet(), og kalt den opp med argument 2 fra print(). Så har vi klikket oss fram til dit fakultet() er blitt kalt opp med argument 0, og har fått returverdi 1.

Rekursjon, bunn av rekursjonskjede

Vi ser at vi har funksjonen fakultet() representert som tre bokser, der hver av boksene utgjør et ledd i kjeden av rekursjonskall, med n lik henholdsvis 2, 1 og 0. Ved neste klikk returnerer siste ledd i kjeden.

Rekursjon etter første retur

Nå er det siste leddet i rekursjonskjeden borte, og returverdien i leddet før er beregnet som 1 · 1 = 1.

Neste klikk gir enda en retur.

Rekursjon etter andre retur

Nå er det enda et ledd i rekursjonskjeden borte, og returverdien i leddet før er beregnet som 2 · 1 = 2.

Neste klikk gir siste retur.

Rekursjon etter tredje retur

Nå er tallet 2 returnert til print() og skrevet ut.

Siden fakultet() returnerer hvis n er 0, kommer vi bare videre i funksjonen hvis n ≠ 0, så else er egentlig overflødig, og vi kan skrive koden slik:

def fakultet(n):
    """Beregner n!"""
    if n == 0:
        return 1
    return n * fakultet(n - 1)

Logikken blir imidlertid tydeligere hvis vi har med else.

Oppgave 1:

Summen av de n første leddene av den harmoniske rekka ${\large\frac{1}{1}} + {\large\frac{1}{2}} + {\large\frac{1}{3}} + \dots + {\large\frac{1}{n}}$ kan vi beregne ved å summere ledd for ledd med denne iterative funksjonen:

def harmonisk(n):
    """Beregner 1/1 + 1/2 + ... + 1/n"""
    rekkesum = 0
    # Adder 1/m til rekkesummen for m fra 1 til n
    for m in range(1, n + 1):
        rekkesum += 1/m
    return rekkesum

Men vi kan også basere oss på en rekursiv definisjon av rekka. For n = 1, er summen definert til 

$S_1 = {\large \frac{1}{1}} = 1$

For n > 1, er summen definert til 1/n + summen av leddene foran:

$S_n = {\large \frac{1}{n}} + S_{n-1}$

For eksempel blir

$S_3 = {\large \frac{1}{3}} + S_{2}$

og

$S_2 = {\large \frac{1}{2}} + S_{1}$

Siden S1 = 1, får vi derved

$S_2 = {\large \frac{1}{2}} + 1 = {\large \frac{3}{2}}$

og

$S_3 = {\large \frac{1}{3}} + {\large \frac{3}{2}} = {\large \frac{11}{6}} \approx 1{,}83333$

Skriv en rekursiv funksjon som bruker denne logikken til å beregne Sn.

Husk at det ikke er noen løkker i den rekursive varianten, den kan skrives så og si rett ut fra definisjonen.

Avrundet til 5 desimaler er riktig svar for n = 10 lik 2,92897.

Se løsningsforslag

Oppgave 2:

Vi har en rekursiv funksjon, summer():

def summer(n):
    if n == 1:
        return n
    else:
        return n + summer(n - 1)

Gjør en analyse av koden med penn og papir, og forklar hva som blir skrevet ut når vi i hovedprogrammet skriver

print(summer(3))

Forklar også hva skjer hvis vi i stedet skriver

print(summer(-3))

Sjekk så om du har tenkt riktig ved å kjøre koden, gjerne i Python Tutor.

Se løsningsforslag

SkjermfilmSe film om rekursjon

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Funksjoner som kaller funksjoner

Hvis vi trekker k elementer fra en mengde på n elementer, angir vi antall kombinasjonsmuligheter som ${\large \binom{n}{k}}$, noe vi kan beregne som ${\large \frac{n!}{k!(n-k)!} }$. I Lotto er det for eksempel ${\large \binom{34}{7}} = {\large \frac{34!}{7!(34-7)!} } = {\small 5 \, 379 \, 616}$ kombinasjonsmuligheter for 7 kuler som trekkes blant 34.

Vi ser at vi i denne formelen må beregne tre fakulteter, n!, k! og (nk)!

En funksjon for å beregne fakulteter står i artikkelen om å lagre funksjoner i moduler:

def fakultet(n):
    """Beregner n!"""
    if n == 0:
        return 1
    else:
        produkt = 1
        for m in range(1, n + 1):
            produkt *= m
        return produkt

Vi har altså mekanismen vi trenger for å skrive en funksjon som beregner ${\large \binom{n}{k}}$. Vi kan kalle opp fakultet() tre ganger med argumenter henholdsvis n, k og nk. Hvis vi, slik det er beskrevet i artikkelen om å lagre funksjoner i moduler, lagrer funksjonen i en modul, trenger vi ikke skrive koden på nytt, vi bare importerer fra modulen. Hvis funksjonen fakultet() ligger i modulen kombinatorikk, kan en funksjon for å beregne ${\large \binom{n}{k}}$ skrives som i eksempel 1.

Eksempel 1:

from kombinatorikk import fakultet
 
def kombinasjon(n, k):
    """Beregner antall kombinasjoner av k blant n"""
    return fakultet(n) // (fakultet(k) * fakultet(n - k))

Grunnen til at vi bruker heltallsdivisjon, er at vanlig divisjon i Python alltid gir flyttall, selv om resultatet er et helt tall, slik som her. Antall kombinasjoner er alltid et helt tall.

Vi kan altså kalle opp en funksjon fra en annen. Vi bruker her funksjonen fakultet() uten å bry oss med hvordan den virker. Vi ber bare fakultet() beregne henholdsvis n!, k! og (nk)!, og bruker resultatene til å beregne ${\large \binom{n}{k}}$.

Vi kan ha hele kjeder med funksjoner som kaller hverandre. Hvis vi i hovedprogrammet skriver

print(kombinasjon(34, 7))

kaller print() opp kombinasjon(), og kombinasjon() kaller opp fakultet() tre ganger. Det er teoretisk ingen grense for hvor lange kjeder av funksjonskall vi kan ha.

Oppgave 1:

Hvis du ikke har gjort det, gjør oppgave 1 i artikkelen om å lagre funksjoner i moduler, slik at du har ei fil som heter kombinatorikk som inneholder funksjonen fakultet().

Åpne så fila kombinatorikk.

Legg inn koden til funksjonen kombinasjon() under fakultet(). Du kan kopiere koden som er vist over, men det er ikke nødvendig å importere fakultet(), siden den ligger i samme fil.

Lagre fila.

Åpne deretter ei ny fil, importer kombinasjon() fra kombinatorikk, og bruk kombinasjon() til å beregne antall kombinasjonsmuligheter når 7 elementer velges fra en mengde på 34.

Skriv ut resultatet. Riktig svar er 5 379 616.

Obs: Den nye fila skal ikke inneholde koden til kombinasjon(), bare hente den gjennom import.

Se løsningsforslag

Oppgave 2:

Hvis vi flipper en mynt, kan den lande med «kron»-siden opp eller «mynt»-siden opp. Sannsynligheten for hvert av de to tilfellene er 0,5. Sannsynligheten for å få k «kron» når vi flipper n ganger er da gitt ved $P(k) = {\large \binom{n}{k}} \cdot (0{,}5)^n$.

Skriv en funksjon som beregner sannsynligheten for få k «kron» i n flipp. Verdier til k og n skal kunne gis inn som argumenter til funksjonen.

Riktig svar for 5 «kron» i 7 flipp er 0,1640625.

Hint: Du vil måtte bruke funksjonen kombinasjon() som du laget i oppgave 1.

Se løsningsforslag

Oppgave 3:

I en binomisk fordeling har vi to mulige utfall i et forsøk. Enten inntreffer en hendelse, eller så inntreffer den ikke. I oppgave 2 var denne hendelsen «kron» på en mynt, med sannsynlighet lik 0,5. Men vi kan også ha hendelser der sannsynligheten ikke er 0,5. Flipper vi for eksempel en tegnestift, kan det være at sannsynligheten for at den havner med spissen opp er 0,7.

I en binomisk fordeling er sannsynligheten for at en hendelse med sannsynlighet p skal inntreffe k ganger i n forsøk gitt ved

$P(k) = {\large \binom{n}{k}} \cdot p^k \cdot {(1-p)}^{(n-k)}$

Utvid funksjonen fra oppgave 2 slik at den ikke er låst til sannsynlighet 0,5, men bruker formelen over. Verdien til p skal kunne gis inn som argument til funksjonen sammen med k og n.

Kall funksjonen binomisk(). Lagre den i fila kombinatorikk, under fakultet() og kombinasjon(), som ligger der fra før.

Åpne deretter ei ny fil, importer binomisk() fra kombinatorikk, og bruk binomisk() til å beregne sannsynligheten for at en hendelse med sannsynlighet 0,2 skal inntreffe 4 ganger i 8 forsøk.

Skriv ut resultatet. Riktig svar avrundet til 5 desimaler er 0,04588.

Obs: Den nye fila skal ikke inneholde koden til binomisk(), bare hente den gjennom import.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Lagre funksjoner i moduler

Vi har i flere artikler importert fra moduler, for eksempel fra math. Men vi kan også lagre vår egen kode i moduler, som vi så kan importere i annen kode.

Vi skal som eksempel skrive en funksjon vi kaller fakultet(), som vi skal lagre i en modul vi kaller kombinatorikk. Python har riktignok en funksjon for å beregne fakultet i modulen math, men vi skal lage vår egen for øvelsens skyld.

Fakultetet til et positivt heltall, n, kan defineres som 1 · 2 · 3 · … · n. Det er også definert at fakultetet til 0 er 1.

Fakultetet til n skriver vi som n!, det vil si n med et utropstegn etter.

Vi har altså at

0! = 1
1! = 1
2! = 1 · 2 = 2
3! = 1 · 2 · 3 = 6
og så videre.

En funksjon som beregner n! kan se ut som i eksempel 1.

Eksempel 1:

def fakultet(n):
    """Beregner n!"""
    if n == 0:
        return 1    
    produkt = 1
    for m in range(1, n + 1):
        produkt *= m
    return produkt

Vi forutsetter at n er et heltall større eller lik 0, og gjør ikke noen verifisering av dette.

Hvis n er 0, returnerer vi 1. Hvis n er 1 eller større, bruker vi akkumulatorvariabelen produkt til å beregne produktet av heltallene fra og med 1 til og med n, og returnerer resultatet etterpå.

Funksjonen kan skrives mer kompakt, som

def fakultet(n):
    """Beregner n!"""
    produkt = 1
    for m in range(2, n + 1):
        produkt *= m
    return produkt

Hvis n er 0 eller 1, går koden ikke inn i løkka, og funksjonen returnerer 1. Hvis n er 2 eller større, gjennomløpes løkka, med samme resultat som den første varianten av funksjonen. Den første varianten er imidlertid lettere å tolke, og gjenspeiler definisjonen av fakultet bedre, så vi kommer til å bruke den videre.

Hvis vi lagrer funksjonen i eksempel 1 i ei fil vi kaller kombinatorikk, blir kombinatorikk en modul vi kan importere. Vi kan importere hele modulen ved å skrive:

import kombinatorikk

Da må vi referere til funksjonen som kombinatorikk.fakultet().

Eller vi kan velge å importere bare fakultet:

from kombinatorikk import fakultet

Da referer vi til funksjonen som fakultet().

Oppgave 1:

Skriv funksjonen fakultet(), slik den er vist over, inn i ei tom fil, og lagre fila med navnet kombinatorikk.

Åpne deretter ei ny fil, importer fakultet() fra kombinatorikk, og bruk fakultet() til å beregne fakultetet til 5.

Skriv ut resultatet. Riktig svar er 120.

Obs: Den nye fila skal ikke inneholde koden til fakultet(), bare hente den gjennom import.

Tips: Det kan være lurt å ha begge filene i samme mappe. Å importere på tvers av mapper kan være utfordrende.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Funksjoner i Python Tutor

Python Tutor er et supert verktøy til å utforske funksjoner, parametere og variabler.

Som eksempel skal vi studere kode fra artikkelen om lokale og globale variabler.

Vi har funksjonen kvadrer(), som kvadrerer verdien til parameteren x:

def kvadrer(x):
    x = x**2

I hovedprogrammet oppretter vi en variabel, x, med verdien 4, kaller opp funksjonen med x som argument, og skriver ut verdien til x etterpå.

x = 4
kvadrer(x)
print(x)

Billedserien under viser hva som skjer når vi linje for linje kjører koden i Python Tutor. Husk at det er den grønne pila som peker på den sist kjørte linja.

Vi kjører koden def kvadrer(x). Vi ser at kvadrer nå opprettes som en variabel med verdi lik ei pil som peker til funksjonen. Innholdet i funksjonen kjøres ikke på dette tidspunktet, neste linje som vil bli kjørt, er x = 4.

Første kodelinje kjørt i Python Tutor

Vi kjører koden x = 4. Vi ser at x nå opprettes som en variabel med verdi lik 4.

Andre kodelinje kjørt i Python Tutor

Vi kjører koden kvadrer(x). Vi ser at funksjonen kvadrer() blir opprettet som en blå boks. Dette kan være litt forvirrende, for egentlig burde nok boksen vært der pila peker. Men vi ser at boksen inneholder den lokale variabelen x. Dette er parameteren x som ble gitt verdien 4 gjennom argumentet x fra hovedprogrammet. 

Tredje kodelinje kjørt i Python Tutor

Vi kjører koden x = x**2 i kvadrer(). Vi ser at x inni kvadrer() nå blir 16, og det blir angitt at funksjonens returverdi er None. Det er fordi funksjoner som ikke har angitt returverdi, returnerer None, som det står i artikkelen om å skrive funksjoner. Vi legger merke til at vi nå har to variabler som begge heter x, men som har forskjellig verdi.

Fjerde kodelinje kjørt i Python Tutor

Det neste som skjer, er at den grønne pila markerer at kjøringen av kvadrer() er utført, og boksen med funksjonen kvadrer() forsvinner. Selv om variabelen kvadrer enda eksisterer, er altså selve funksjonen borte. Det betyr blant annet at variabelen x i kvadrer() ikke lenger finnes.

Femte kodelinje kjørt i Python Tutor

Vi kjører koden print(x). Vi ser at tallet 4 dukker opp i feltet «Print Output».

Sjette kodelinje kjørt i Python Tutor

Oppgave 1:

Bildet under viser koden fra oppgave 1 i artikkelen om lokale og globale variabler, lagt inn i Python Tutor. Vi har klikket oss fram så langt at verdien til returvariabelen er beregnet, men funksjonen har enda ikke returnert. Tegn en skisse av hvordan du tror høyre side av bildet i Python Tutor ser ut. Altså hvilke bokser som finnes, hvilke variabler som finnes, og hvilken verdi de har. Sjekk så i Python Tutor om du har rett.

Oppgave i Python Tutor

Se løsningsforslag

Funksjonsparametere

Antall parametere

Funksjoner har ofte parametere, men de trenger ikke ha det. Da har vi et sett tomme parenteser i funksjonshodet.

Funksjonen i eksempel 1 under har ingen parametere, og benytter funksjonen randint() i modulen random til å simulere et myntkast. Den returnerer så strengen «mynt» eller «kron».

Eksempel 1:

from random import randint

def myntkast():
    """Simulerer et myntkast og returnerer "mynt" eller "kron"."""
    if randint(0, 1) == 0:
        return "mynt"
    else:
        return "kron"

Hvis vi i hovedprogrammet skriver print(myntkast()), vil vi få skrevet ut enten mynt eller kron.

Oppgave 1:

Skriv en funksjon som simulerer terningkast, det vil si genererer tilfeldige tall mellom 1 og 6, og returnerer resultatet.

Test funksjonen ved å skrive ut verdien den returnerer.

Se løsningsforslag

Rekkefølge på parametere

Hvis vi ikke angir noe annet, koples argumentene vi gir inn til en funksjon, til parameterne i den rekkefølgen de står. La oss for eksempel si at vi har kode som beregner $\sqrt[\Large n]{x}$ ved å benytte at $\sqrt[\Large n]{x} = x^{\Large \frac{1}{n}}$, som i eksempel 2:

Eksempel 2:

def nrot(x, n):
    """Beregner n'te rot av x."""
    return x**(1/n)

Hvis vi så i hovedprogrammet skriver nrot(8, 3), vil parameteren x bli 8 og n bli 3, så vi får regnet ut $\sqrt[\Large 3]{8} = 2$.

Bytter vi om argumentene og skriver nrot(3, 8), vil parameteren x bli 3 og n bli 8, så vi får regnet ut $\sqrt[\Large8]{3}\approx1{,}1472$.

Navngi parametere

Det er mulig å angi parameternavn sammen med argumentene. Hvis vi har funksjonen nrot() som i eksempel 2, kan vi i hovedprogrammet navngi parameterne ved å skrive nrot(x = 8, n = 3) eller nrot(n = 3, x = 8).

Når vi navngir parameterne, spiller ikke rekkefølgen på argumentene noen rolle, fordi navnene overstyrer rekkefølgen.

Som hovedregel bør vi imidlertid holde oss til å bruke rekkefølge til å kople argumenter og parametere. Bruker vi navn, vil koden slutte å virke hvis vi endrer navn på parameterne. Innebygde Python-funksjoner og funksjoner i moduler tillater ofte heller ikke at vi navngir parameterne. Skriver vi for eksempel abs(x = -3), får vi en feilmelding, selv om parameteren i abs() heter x.

Navngitte parametere bør vi reservere til tilfeller der en parameter har en spesiell betydning. Et godt eksempel er Python-funksjonen enumerate(), som vi beskriver i artikkelen om å gjennomløpe lister. enumerate() tillater oss å angi en startverdi for løpenumre ved å navngi parameteren start, og for eksempel skrive start = 1.

Argumenter med parameternavn kalles keyword arguments på engelsk. Det forklarer feilmeldingen "abs() takes no keyword arguments", som vi får hvis vi skriver abs(x = -3). Argumenter uten parameternavn kalles positional arguments.

Standardverdier for parametere

Det er mulig å angi standardverdier for parametere i funksjoner. Standardverdier angis sammen med parameterne i funksjonshodet, og brukes hvis funksjonen blir kalt opp uten argumenter til parameterne. Eksempel 3 viser koden til funksjonen nrot() fra eksempel 2, med 2 satt som standardverdi for parameteren n.

Eksempel 3:

def nrot(x, n = 2):
    """Beregner n'te rot av x."""
    return x**(1/n)

Hvis vi i hovedprogrammet skriver nrot(81), blir parameteren x satt til 81, og siden det ikke finnes noe argument til parameteren n, blir n satt til standardverdien 2. Med andre ord beregnes andrerot, altså kvadratrot, av 81, som er 9.

Hvis et argument koples til en parameter med standardverdi, overstyres standardverdien, og parameteren får verdien til argumentet. Har vi for eksempel funksjonen nrot() som i eksempel 3, og skriver nrot(81, 4), i hovedprogrammet, overstyrer 4 standardverdien 2, og vi får beregnet fjerderot av 81, som er 3.

Standardverdier kalles default values på engelsk.

Parametere med standardverdier må komme til høyre for alle parametere uten standardverdier. Gjør de ikke det, vil det ikke være en entydig kopling mellom argumenter og parametere. Hvis vi for eksempel bytter om på parameterne i eksempel 3, og definerer nrot() som 

def nrot(n = 2, x):
    return x**(1/n)

vil det ikke være mulig å avgjøre om et argument skal koples til parameteren n eller ikke. En slik definisjon vil gi feilmeldingen "SyntaxError: non-default argument follows default argument".

Har vi flere parametere med standardverdier, koples de med argumentene fra venstre mot høyre.

Eksempel 4:

Sannsynlighetstettheten i en normalfordeling er gitt ved

$f(x) = \frac{\displaystyle 1}{\displaystyle \sigma \sqrt{2\pi}} {\; \large e}^{{\large -} \, \frac{\displaystyle (x – \mu)^2}{\displaystyle 2 \sigma^2}}$

der μ er forventningsverdi og σ er standardavvik.

Så skal vi skrive en funksjon, normal(), som bruker denne formelen til å beregne f(x) basert på parameterne x, μ (my) og σ (sigma). Ofte brukes imidlertid et spesialtilfelle av normalfordelingen som kalles standard normalfordeling, der μ = 0 og σ = 1. Vi lar derfor 0 og 1 være standardverdier for henholdsvis my og sigma.

from math import pi, exp, sqrt
 
def normal(x, my = 0, sigma = 1):
    """Beregner sannsynlighetstettheten til x"""
    eksp = -(x - my)**2 / (2 * sigma**2)
    return 1/(sigma * sqrt(2 * pi)) * exp(eksp)

Vil vi for eksempel beregne sannsynlighetstettheten til 2 i en standard normalfordeling, trenger vi ikke oppgi noe argument til my eller sigma, siden vi skal bruke standardverdiene. Vi kan bare skrive normal(2).

Skriver vi normal(2, 4), blir my satt til 4, mens sigma beholder standardverdien 1.

Skriver vi normal(2, 4, 3), blir my satt til 4, og sigma blir satt til 3.

I funksjonen normal() i eksempel 4 har imidlertid parameterne my og sigma, en spesiell betydning, slik at det vil være naturlig å navngi dem. For eksempel ved å skrive normal(2, my = 4) eller normal(2, my = 4, sigma = 3). Da kan vi også gi en verdi til sigma og la my beholde standardverdien, for eksempel ved å skrive normal(2, sigma = 3).

Oppgave 2:

Skriv kode som bruker funksjonen normal() i eksempel 4 til å beregne sannsynlighetstettheten til 3 i en normalfordeling der sigma er 5, men my beholder standardverdien 0.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Skrive funksjoner

Vi har i mange artikler benyttet oss både av Pythons innebygde funksjoner og funksjoner importert fra moduler. Her skal vi se hvordan vi kan skrive våre egne funksjoner.

I utgangspunktet kan vi tenke på en funksjon som en boks der vi gir inn noe og får ut noe. Noe à la en matematisk funksjon, f, der vi gir inn et tall, x, og får ut en beregnet funksjonsverdi, f(x). Hvis vi for eksempel har f(x) = 5x2, vil alle verdier vi putter inn i funksjonen, bli opphøyd i andre og multiplisert med 5 før de blir sendt ut igjen.

Eksempel 1 viser hvordan funksjonen f(x) = 5x2 kan skrives i Python.

Eksempel 1:

def f(x):
    return 5 * x**2

En funksjon definerer vi i Python ved å skrive kodeordet def etterfulgt av funksjonsnavnet. I eksempel 1 er funksjonsnavnet f. Deretter kommer et sett parenteser og et kolon. Mellom parentesene kan vi ha variabler. Slike variabler kalles parametere. Koden i eksempel 1 har en enkelt parameter, x. Når vi aktiverer funksjoner, bruker vi argumenter for å gi verdier til parameterne. 

Den første linja i en funksjon kalles funksjonshodet. I funksjonshodet angir vi navnet på funksjonen og hvilke parametere den har. Thonny markerer navnet på funksjoner med mørkeblå skrift.

Etter funksjonshodet følger en innrykket kodeblokk, som vi kaller funksjonskroppen. Koden i funksjonskroppen gjennomløpes når funksjonen aktiveres. Inni funksjonskroppen vil vi ofte ha kodeordet return, som angir hva som skal sendes ut igjen av funksjonen. I eksempel 1 er dette verdien vi får når vi beregner 5x2. Det kaller vi en returverdi.

Hvis vi nedenfor funksjonen i eksempel 1, uten innrykk, skriver

y = f(3)
print(y)

vil Python svare 45.

Vi bruker ikke innrykk på disse linjene fordi de ikke hører med til funksjonen.

I stedet for å si «nedenfor funksjonen» vil vi fra nå av si «i hovedprogrammet».

Når Python kjører koden y = f(3), aktiveres funksjonen f med tallet 3 som argument. Siden funksjonens parameter er x, vil x bli satt lik 3. Så blir 3 brukt i uttrykket 5 * x**2, som da blir 5 · 32 = 45. Returverdien er altså 45.

I stedet for å si at vi aktiverer en funksjon, sier vi gjerne at vi kaller funksjonen, eller kaller opp funksjonen.

Vi har tidligere lært at et likhetstegn i Python betyr at vi skal ta verdien til høyre for likhetstegnet og tildele til variabelen til venstre. Så y = f(3) betyr at returverdien fra funksjonen f skal tildeles til variabelen y. print(y) skriver deretter ut verdien til y, på samme måte som vi har gjort i mange andre artikler.

Vi kunne droppet variabelen y, og bare skrevet

print(f(3))

Men variabelen y gjør logikken tydeligere.

Eksempel 1 var lagt tett opp til den matematiske funksjonen f(x) = 5x2, men funksjoner trenger ikke hete f, og parameteren trenger ikke hete x. I programmering bruker vi gjerne litt mer beskrivende navn. For navn på funksjoner gjelder samme regler som for navn på variabler. Navnene kan bare inneholde bokstaver, tall og understreking, og kan ikke starte med tall. Ifølge stilguiden PEP 8 bør vi også bare bruke små bokstaver i funksjonsnavn, på samme måte som i variabelnavn, og ikke starte med understreking.

Eksempel 2:

Vi skal lage en funksjon som regner om fra grader Fahrenheit til grader Celsius ved hjelp av formelen $c = {\large \frac{5(f-32)}{9}}$, der f er grader Fahrenheit og c grader Celsius.

Funksjonen kan se slik ut:

def fahr_til_cels(fahrenheit):
    celsius = 5 * (fahrenheit - 32) / 9
    return celsius

Her er funksjonsnavnet fahr_til_cels og parameteren fahrenheit. celsius er en lokal variabel som tilhører funksjonen.

Vi kan også droppe variabelen celsius, og skrive funksjonen mer kompakt som

def fahr_til_cels(fahrenheit):
    return 5 * (fahrenheit - 32) / 9

Det blir et spørsmål om smak og behag hva vi velger.

Hvis vi i hovedprogrammet, uten innrykk, skriver

print(f"{fahr_til_cels(100):.02f}")

svarer Python 37.78.

I eksempel 2 har vi ikke brukt f og c som variabelnavn, men valgt det mer beskrivende fahrenheit og celsius, for å gjøre koden mer lesbar for mennesker.

Vi har tidligere brukt kommentarer mye til å forklare hva Python-kode gjør. For funksjoner finnes det en konvensjon om at koden skal beskrives ved hjelp av en såkalt docstring. En docstring er en tekst på første linje i funksjonskroppen, angitt mellom triple anførselstegn. For eksempel slik, for funksjonen i eksempel 2:

def fahr_til_cels(fahrenheit):
    """Konverterer fra grader Fahrenheit til grader Celsius."""
    return 5 * (fahrenheit - 32) / 9

Thonny markerer en docstring med grønn skrift, slik som andre strenger.

Python-funksjonen help() benytter seg av en funksjons docstring. Hvis vi i hovedprogrammet skriver

help(fahr_til_cels)

svarer Python:

Help on function fahr_til_cels in module __main__:

fahr_til_cels(fahrenheit)
Konverterer fra grader Fahrenheit til grader Celsius.

Oppgave 1:

Skriv en Python-funksjon, cels_til_fahr(), som regner om fra grader Celsius til grader Fahrenheit ved hjelp av formelen $f = {\large \frac{9c}{5}} + 32$, der f er grader Fahrenheit og c grader Celsius. Legg inn en passende docstring i funksjonen.

For å teste funksjonen kan du skrive print(f"{cels_til_fahr(10):.02f}") i hovedprogrammet, og sjekke at Python skriver ut 50.00.

Se løsningsforslag

En funksjon kan ha et vilkårlig antall parametere, vi lister dem opp mellom parentesene i funksjonshodet, atskilt med komma.

Eksempel 3:

Funksjonen under beregner og returnerer gjennomsnittet av tre tall:

def snitt(tall_1, tall_2, tall_3):
    """Beregner gjennomsnittet av tre tall."""
    return (tall_1 + tall_2 + tall_3) / 3

Eksempel 4:

Funksjonen under finner og returnerer det største av to tall:

def storst(tall_1, tall_2):
    """Finner det største av to tall."""
    if tall_1 > tall_2:
        return tall_1
    else:
        return tall_2

Vi har ikke presisert hva som skal skje hvis de to tallene er like, men det er underforstått at det felles tallet da skal returneres. Dette tilfellet fanges opp av else.

I eksempel 4 ser vi at vi gjerne kan ha return flere plasser.

Oppgave 2:

Skriv en Python-funksjon som finner og returnerer det minste av to tall. Test den på en måte du synes er fornuftig.

Se løsningsforslag

I funksjoner angir vi ofte returverdier, men vi trenger ikke gjøre det. En funksjon som ikke har angitt returverdi, returnerer None. None er ikke en variabel, men et kodeord i Python. Den innebygde Python-funksjonen print() returnerer for eksempel None.

En funksjon bør bare returnere verdier av én enkelt datatype. Det vil si at funksjonen for eksempel enten returnerer tall eller strenger, men ikke tall i noen tilfeller og strenger i en annen.

Vi bør unngå innlesing og utskrift inni funksjoner. Dette vil låse funksjonen til direkte kommunikasjon med brukeren, og derved gjøre den mindre generell og anvendelig. Innlesing legger vi i hovedprogrammet, og sender det brukeren skriver inn, som et argument til funksjonen. Utskrift legger vi også i hovedprogrammet, og lar funksjonen sende det som skal skrives ut, som en returverdi.

SkjermfilmSe film om funksjoner

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

I noe litteratur kalles parametere for «formelle parametere» og argumenter for «aktuelle parametere». Disse betegnelsene unngår vi for å ikke skape forvirring.

Eksponentialfunksjoner

Eksponentialfunksjonenes egenskaper

I artikkelen om potensfunksjoner ser vi at en potensfunksjon er på formen f(x) = axb, der a og b er reelle tall.

I potensfunksjonen opphøyer vi en variabel i et tall. Hvis vi snur rundt på det og i stedet opphøyer et tall i variabelen, får vi en eksponentialfunksjon, som er på formen f(x) = kax, der k og a er reelle tall, a > 0.

a kalles funksjonens vekstfaktor fordi den styrer hvor mye funksjonsverdien endrer seg når x endrer seg.

k kalles funksjonens startverdi, for ved starten, altså når x = 0, får vi f(0) = ka0 = k · 1 = k.

I en potensfunksjon opphøyer vi altså variabelen i en konstant, mens vi en eksponentialfunksjon opphøyer en konstant i variabelen.

Eksempel på eksponentialfunksjoner er $2^{\large x}$ og $3\cdot \large (\frac{1}{5})^x$.

Definisjonsmengden til eksponentialfunksjoner er alle reelle tall, $D_f = \mathbb R$.

Verdimengden til eksponentialfunksjoner er alle positive tall, $V_f = (0, \infty)$.

Eksponentialfunksjoner med a > 1 går mot uendelig når x går mot uendelig, og mot 0 når x går mot minus uendelig.

Eksponentialfunksjoner med 0 < a < 1 går mot 0 når x går mot uendelig, og mot uendelig når x går mot minus uendelig.

Eksempel 1:

Bildet under viser grafene til funksjonene

$5^{\large x}$, grønn graf.

$2^{\large x}$, blå graf.

$1^{\large x}$, rød graf.

$\large(\frac{1}{2})^x$, oransje graf.

$\large(\frac{1}{5})^x$, lyseblå graf.

Grafene til et utvalg eksponentialfunksjoner

Vi ser at:

Alle grafene går gjennom punktet (0,1).

Alle grafene går gjennom punktet (1, a). Det vil si at 2x går gjennom punktet (1, 2), 5x går gjennom punktet (1, 5), etc.

Funksjonsverdien er alltid større enn 0.

Grafer med vekstfaktor a > 1 blir krappere jo større a er. De stiger mot høyre, har funksjonsverdi mellom 0 og 1 når x < 0 og funksjonsverdi større enn 1 når x > 0.

Grafer med vekstfaktor a = 1 er en rett linje.

Grafer med vekstfaktor a < 1 blir krappere jo mindre a er. De synker mot høyre, har funksjonsverdi større enn 1 når x < 0 og funksjonsverdi mellom 0 og 1 når x > 0.

Bruker vi regnereglene for potenser, ser vi at $(\frac{\displaystyle 1}{\displaystyle a})^{\large x} = \frac{\displaystyle 1^{\large x}}{\displaystyle a^{\large x}} = \frac{\displaystyle 1}{\displaystyle a^{\large x}} = a^{\large −x}$. Det betyr at $(\frac{\displaystyle 1}{\displaystyle 5})^{\large x}$ kan skrives som $5^{\large −x}$ og $(\frac{\displaystyle 1}{\displaystyle 2})^{\large x}$ som $2^{\large −x}$. Det forklarer at grafparet $(\frac{\displaystyle 1}{\displaystyle 5})^{\large x}$ og $5^{\large x}$ og grafparet $(\frac{\displaystyle 1}{\displaystyle 2})^{\large x}$ og $2^{\large x}$ i eksempel 1 er speilversjoner av hverandre, med y-aksen som symmetriakse.

Eksempel 2:

Setter vi kr 1000 i banken, og får 2 % rente pr. år, har vi etter 1 år

kr 1000 · 1,02

Siden vi får rente av rentene, har vi etter 2 år

kr (1000 · 1,02) · 1,02, altså kr 1000 · (1,02)2

Etter 3 år har vi

kr ((1000 · 1,02) · 1,02) · 1,02, altså kr 1000 · (1,02)3

og etter x år har vi

kr 1000 · (1,02)x

Vi ser at dette er en eksponentialfunksjon på formen f(x) = kax, der k = 1000 og a = 1,02. Vekstfaktoren a er her altså 1 + 2 % = 1 + 0,02 = 1,02.

Tallet e

I eksempel 2 la vi til renter en gang hvert år. Men vi kan jo også tenke oss at vi gjør det hver måned. Da vil vi få rentes rente allerede etter 1 måned. Legger vi til renter hver dag, får vi rentes rente etter 1 dag. Slik kan vi fortsette: Hver time, hvert minutt, hvert sekund. Til slutt ender vi opp med at vi legger til rente kontinuerlig. Og kontinuerlig rente er faktisk noe som brukes i virkeligheten. Dersom vi setter inn kr 1000 til 2 % kontinuerlig rente, vil vi etter x år ha kr 1000 · e0,02x. Dette er en eksponentialfunksjon på formen f(x) = ax, men her er ikke a et rasjonalt tall som vi har sett tidligere, for eksempel 2 og 1,02, men et irrasjonalt tall som heter e. e kalles Euler-tallet, oppkalt etter matematikeren Leonard Euler. e opptrer i mange sammenhenger både i naturen og i matematikken. De første sifrene til e er 2,71828.

Oppgave 1:

Under vises fire grafer i grønt, blått, rødt og oransje. De tilhører funksjonene

$f(x) = 2^{\large x}$

$g(x) = e^{\large x}$

$h(x) = {\large (\frac{1}{3})}^{\large x}$

$p(x) = x^{\large 3}$

men ikke i den rekkefølgen

Diverse grafer

  1. Hvilken av funksjonene er ikke en eksponentialfunksjon?
     
  2. Hvilken funksjon hører til hvilken graf?

Se løsningsforslag

Eksponentielle modeller

I artikkelen om representasjonsformer ser vi på lineære modeller. I en lineær modell er endringen konstant over tid. Vi trenger for eksempel en lineær modell for å beregne hvor langt vi har kjørt etter en viss tid når farten er konstant. I en lineær modell vil grafen være en rett linje, og eventuelle målepunkter vil ligge i nærheten av denne linjen.

I en eksponentiell modell, derimot, vil økningen være avhengig av hvor mye vi har fra før. La oss ta for oss eksempel 2 på nytt. Vi setter inn kr 1000 i banken med en årlig rente på 2 %. Hvis vi ikke hadde fått rentes rente, ville den årlige tilveksten vært konstant, kr 20. Vi ville da hatt en lineær modell. Med rentes rente vil tilveksten første år også være kr 20, men mellom år 19 og 20, for eksempel, vil den være

kr 1000 · (1,02)20 − 1000 · (1,02)19 ≈ kr 29,13.

Mellom år 99 og år 100 vil den være

kr 1000 · (1,02)100 − 1000 · (1,02)99 ≈ kr 142,05.

Mellom år 999 og år 1000 vil den være

kr 1000 · (1,02)1000 − 1000 · (1,02)999 ≈ kr 7 809 110 816,83.

Grafene under viser hvor hvordan beløpet i banken endrer seg over 200 år. Den blå grafen uten rentes rente – altså en lineær modell, den røde grafen med rentes rente – altså en eksponentiell modell.

Grafer som viser vekst av innskuddsbeløp med og uten rentes rente

Eksempel 3:

I eksempel 2 har vi en vekstfaktor, a, som er større enn 1, og grafen stiger derfor mot høyre. Et eksempel der vekstfaktoren ligger mellom 0 og 1, er følgende:

Vi kjøper en bil til kr 250 000 som faller i verdi med 13 % i året. Et verdifall på 13 % betyr en vekstfaktor på 1 − 0,13 = 0,87. Grafen til denne modellen er vist under, for de første 12 årene:

Graf som viser verdifall etter en eksponentiell modell

Siden vekstfaktoren a < 1, synker grafen mot høyre. Vi ser at etter x = 5 år har bilens verdi sunket til omtrent det halve. Dette er et realistisk prisfall på nye biler.

Oppgave 2:

Elevtallet på en skole i 10 år etter 1989 er gitt i tabellen under:

År etter 1989 0 1 2 3 4 5 6 7 8 9 10
Antall elever 100 110 121 133 146 161 177 195 214 236 259
  1. Foreslå en matematisk modell for elevtallet som funksjon av antall år etter 1989. Begrunn hvorfor du velger en lineær eller eksponentiell modell.
     
  2. Samme modell er gyldig for hele perioden mellom 1985 og 2005. Bruk modellen til å anslå elevtallet etter 12 år, og 2 år før 1989.

Se løsningsforslag

Oppgave 3:

Den årlige prisstigningen på boliger i et område har vært 6 % de siste fem årene, og forventes å være 6 % også de neste to årene.

  1. Sett opp en matematisk modell for prisen på en bolig i denne perioden, når boligen koster 3 millioner kroner i dag.
     
  2. Hva vil boligen koste om 2 år ifølge denne modellen?
     
  3. Hva kostet boligen for 4 år siden ifølge denne modellen?

Se løsningsforslag

Kilder

    • Gulliksen, T. & Hole, A. (2010). Matematikk i praksis. Universitetsforlaget
    • Selvik, B. K. (2007). Algebra og funksjonslære. Caspar forlag