Løkker#

Læringsutbytte

Etter å ha arbeidet med dette temaet, skal du kunne:

  1. bruke while- og for-løkker til å gjenta kode

  2. tegne geometriske mønstre med turtle-grafikk

  3. beregne rekkesummer

  4. løse naturvitenskapelige problemer med løkker

Definisjon#

Den kanskje viktigste strukturen i programmering er løkker. De lar datamaskinen gjenta en operasjon så mange ganger vi vil. Det er dette som er datamaskinens styrke.

Løkker

En løkke er en struktur som gjør at vi kan gjenta kode. Ofte skiller vi mellom en telleløkke, som gjentar noe et visst antall ganger, og en tilstandsløkke, som gjentar seg så lenge noe er sant. I Python heter disse henholdsvis for-løkke og while-løkke.

Skilpaddegrafikk#

Vi skal se hvordan løkker fungerer ved å introdusere deg for en skilpadde. Han heter Gunnar. Her er han:

Gunnar følger enkle kommandoer, som “forward”, “backward”, “right” og “left”.

Underveisoppgave

Endre programmet ovenfor slik at Gunnar tegner en trekant. Hva er sammenhengen mellom vinkelen som skilpadden snur og vinklene i trekanten?

Skilpadder i løkker#

Å tegne en trekant krever få linjer kode, men hva hvis du vil tegne en åttekant, en førtitokant, eller en nittisekskant? Det er slitsomt og kjedelig å skrive samme kommando hundrevis av ganger. Og det er totalt unøvdendig. Vi bruker nemlig løkker til å gjenta kode, for eksempel slik:

Underveisoppgave

Prøv å forklare hvordan programmet ovenfor fungerer. Hva tror du “for i in range(n)” betyr? Hva tror du i er?

Underveisoppgave

Modifiser programmet ovenfor slik at skilpadden Gunnar tegner en 20-kant.

Oppgave

Få Gunnar til å tegne et menneske eller en blomst.

While-løkker#

Vi har to typer løkker i Python: while-løkker (tilstandsløkker) og for-løkker (telleløkker). While-løkker gjentar noe helt til et kriterium er nådd. Her er et eksempel:

Programmet kjører så lenge variabelen partall har en verdi som er mindre enn eller lik 10. Alt som er rykket inn, gjentas hver gang løkka går. Programmet skriver derfor ut alle positive partall (og 0) som er mindre enn eller lik 10.

Underveisoppgave

Modifiser programmet ovenfor slik at programmet skriver ut alle positive oddetall under 10.

For-løkker#

I for-løkker lager vi en teller eller tellevariabel som går igjennom en slags liste med tall. Denne listeliknende tallmengden kan vi lage med funksjonen range. Her er noen eksempler:

kommando

liste

range(3)

[0, 1, 2]

range(2,4)

[2, 3]

range(1,8,2)

[1, 3, 5, 7]

Underveisoppgave

Forklar hvordan range-funksjonen fungerer med utgangspunkt i eksemplene ovenfor.

Et enkelt eksempel på en for-løkke er slik:

for i in range(5):
    print(i)

Vi kan også gjøre noe mer enn å telle i løkka:

a = 2
for i in range(5):
    print(i)
    a = a + i
    
print(a)

Så hva betyr dette? Helt enkelt betyr det at alt som er rykket inn (med tab eller fire mellomrom), blir gjentatt 5 ganger. Operasjonen der vi oppdaterer a er

Hvis vi skal forklare litt mer presist hva som skjer, kan vi si at range-funksjonen lager en tallmengde [0, 1, 2, 3, 4], og at i blir tilordnet hver av disse verdiene etter tur. Første gang løkka går, er \(i = 0\). Da printes denne verdien, og \(a = 2 + 0 = 2\). Deretter gjentas alt inni løkka på nytt, og i får verdien 1. Så gjentas det som står rykket inn en gang til: Vi printer 1, og \(a = 2 + 1 = 3\). Slik fortsetter det til og med \(i = 4\). Når i har hatt alle verdiene i tallmengden, avsluttes løkka.

Underveisoppgave

Vi kan systematisere løkkene med en løkketabell. Den holder styr på hva verdien til de ulike variablene er underveis i løkka. Fyll inn resten av løkketabellen for løkka ovenfor.

Løkkerunde

i

a

Startverdi

-

2

1

0

2

2

1

3

3

4

5

I for-løkker kaller vi ofte tellevariabelen for i, j, k eller liknende, men den kan egentlig hete hva som helst. I tillegg trenger vi ikke å gjøre noe med tellevariabelen. Mange ganger brukes den bare for å telle. Her er et eksempel:

Underveisoppgave

Modifiser programmet ovenfor slik at det skriver ut hvor mye penger du har etter et visst antall år. Lag en variabel som inneholder antall år, og som du bruker i løkka. Legg også inn hensiktsmessige kommentarer.

Å tenke i løkker#

Løkker kan brukes til alt fra å summere tallrekker i matematikk til å finne ut hvor mange harer det er i økosystem etter en viss tid, finne posisjonen til et legeme eller utforske hvordan kjemiske reaksjoner foregår. Når vi skal bruke løkker, må vi dele opp problemer slik at de kan utføres trinnvis. I matematikk og naturvitenskapelige fag er det ofte slik at vi representerer sammenhenger med kontinuerlige funksjoner og formler, men når vi løser problemene med løkker, gjør vi det trinnvis. For hvert trinn, gjør vi en bestemt operasjon. Dette kalles å løse noe iterativt, fordi iterasjon betyr gjentakelse. Litt mer uformelt kan vi kalle det å løse noe trinnvis med små, repeterende operasjoner “å tenke i løkker”. La oss se på noen eksempler fra både matematikk og naturvitenskap.

Tallfølger#

En tallfølge er en oppramsing av tall som kan være enten endelig eller uendelig. Et eksempel på en endelig tallfølge er 2, 4, 6, 8, 10, som er en tallfølge av de 5 første partallene. En berømt uendelig tallfølge er Fibonacci-tallene: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …, der de tre siste prikkene sier oss at rekka er uendelig lang. Fibonacci-tallene starter på 1, og hvert tall er deretter summen av de to foregående tallene.

Underveisoppgave

Beskriv mønsteret i følgen 1, 4, 7, … og skriv opp det neste tallet i følgen.

Hvert tall i en tallfølge kalles et ledd. Vi kan beskrive hvert ledd i en følge med symboler, for eksempel \(a_n\), der n er en indeks som beskriver leddnummeret. I tallfølgen 1, 3, 5, 7, … kan for vi for eksempel si at \(a_1 = 1\) og \(a_2 = 3\). Poenget med å beskrive en følge med symboler, er at vi kan lage formler for hvert generelle ledd \(a_n\). Følgen 1, 3, 5, 7, … kan beskrives med den generelle formelen \(a_{n+1} = a_n + 2\), der \(a_1 = 1\). En slik formel kalles en rekursiv formel fordi vi tar utgangspunkt i en tidligere verdi for å regne ut neste verdi.

Underveisoppgave

Forklar hvorfor \(a_{n+1} = a_n + 2\) er det samme som \(a_n = a_{n-1} + 2\).

Når vi først har kommet fram til en generell formel for en tallfølge, kan vi finne det n-te leddet ved hjelp av programmering. Vi bruker tallfølgen 1, 3, 7, 15, 31, … som eksempel. Denne følgen kan vi beskrive med formelen \(a_{n+1} = a_n + 2^n\). La oss finne det hundrede leddet i denne følgen:

Underveisoppgave

Forklar hva som skjer i programmet ovenfor. For å sjekke om programmet vårt fungerer, kan det være lurt å beregne tall som vi kjenner i følgen. Prøv å finne tall nr. 3, 4 og 5 for å sjekke at programmet gjør det det skal.

Underveisoppgave

Endre programmet ovenfor slik at det finner det 20. elementet i tallfølgen 1, 5, 11, 19, 29, …

Tallrekker#

Når vi beskriver en tallfølge som en serie med tall som adderes med hverandre, kaller vi det for en rekke. For eksempel er 1, 5, 11, 19, 29, … en tallfølge, mens 1 + 5 + 11 + 19 + 29 + … er en tallrekke. Vi kan summere slike rekker, selv om de er uendelige.

Noen rekkesummer går mot uendelig mens andre går mot en bestemt verdi. Vi kan utlede formler for å regne ut summen av slike rekker, men vi kan også lage programmer som gjør det. La oss si at vi har denne rekka (ei såkalt uendelig geometrisk rekke):

\(1 + \frac{2}{3} + \frac{4}{9} + \frac{8}{27} + ...\)

Vi kan regne ut summen av denne rekka ved å først kjenne igjen et mønster i tallmengden. Vi kan vise at det n-te tallet i rekka er \(\left(\frac{2}{3}\right)^n\). Deretter kan vi programmere ei løkke som legger sammen så mange av disse tallene som mulig. Vi tilnærmer altså den uendelige rekka med et endelig antall ledd. Det kan vi gjøre slik

N = 100
S = 0

for n in range(N):
    S = S + (2/3)**n  # summen er lik forrige sum + sum av nytt tall
print(S)
2.9999999999999987

Den nøyaktive, analytiske verdien vi kan få dersom vi uleder en summeformel for rekka, er 3 (uten desimaltall). Men når vi legger sammen de 100 første tallene i programmet ovenfor, får vi faktisk et tall som er ganske nærme. Det er ikke verst! Vi får sjeldent helt nøyaktige svar når vi bruker slike metoder, men vi kommer ganske nærme.

Naturvitenskapelige problemer#

Det som er ekstra morsomt med å løse problemer iterativt, er at vi ofte løser ting på samme måte på tvers av ulike fag. Se bare på de tre ulike programmene nedenfor, fra fagområdene fysikk og biologi. Du kan også sammenlikne med det du har lært om følger og rekker.

# Fysikkeksempel

tid = 10    # slutt-tid i sekunder
dt = 0.001  # tidssteg mellom hver iterasjon
t = 0       # start-tid
a = 9.8     # akselerasjonen til et legeme

v = 0       # startfart i m/s
s = 0       # startposisjon i m

while t <= tid:
    v = v + a*dt # bevegelsesformel for hastighet (konstant a)
    s = s + v*dt # bevegelsesformel for posisjon (konstant v)
    t = t + dt   # oppdaterer tida med dt

print("I løpet av", tid, "sekunder falt steinen", s, "m.")
I løpet av 10 sekunder falt steinen 490.01960014606396 m.

Underveisoppgave

Beskriv hva programmet ovenfor gjør. Problemet kan løses relativt enkelt ved hjelp av formler, men kan du tenke deg hvorfor denne måten å gjøre det på kan være nyttig likevel?

B0 = 100          # antall bakterier ved t = 0
B = B0
antall_timer = 30 # slutt-tid i timer
t = 0             # start-tid
vekst = 1.20      # vekstfaktor

for i in range(antall_timer):
    B = B*vekst

print("Antall bakterier er:", int(B))
Antall bakterier er: 23737

Underveisoppgave

Forklar hva programmet ovenfor gjør. Dette programmet gjør det samme som å sette t = 30 inn i funksjonen \(B(t) = 100\cdot 1.20^t\). Hva beskriver denne funksjonen, og hvorfor kan det være nyttig å lage et program for å regne ut dette når det er såpass lett å løse analytisk?

De to programmene ovenfor benytter samme løsningsstrategi: Ut fra en startbetingelse (startposisjon og startfart eller antall bakterier til å begynne med) regner vi ut utviklingen over tid i et system (enten posisjonen til en stein som faller, eller antall bakterier til slutt), gitt noen premisser for systemet (henholdsvis akselerasjon og vekstfaktor). Det er altså mange likheter mellom måten vi løser problemene på.

Fordelen ved å bruke programmering til å løse slike problemer, er at det er lett generaliserbart. Selv om det finnes formler som ganske nøyaktig kan beskrive posisjonen til steinen ved enhver tid, er det ikke så lett å forutse posisjonen til en fallskjermhopper, der akselerasjonen varierer ganske mye, eller veksten til bakterier hvis den avhenger av temperatur. Alt dette er relativt enkelt å legge til i programmene våre, mens de analytiske (formelbaserte) løsningene blir veldig kompliserte, og ofte uløselige.

For eksempel kan vi enkelt legge inn en temperaturavhengighet i bakterieveksten vår slik:

B = 100
antall_timer = 30
vekst_liste = [1.20, 1.30, 1.42, 1.48]
temperatur = 30

for i in range(antall_timer): 
    if 30 <= temperatur <= 40: 
        vekst = vekst_liste[0]
    elif 40 < temperatur < 52: 
        vekst = vekst_liste[1]
    elif 53 <= temperatur <= 65: 
        vekst = vekst_liste[2]
    else:
        vekst = vekst_liste[3] 
    B = B*vekst 
    temperatur = temperatur + 1

print("Antall bakterier er:", int(B))
Antall bakterier er: 229430

Nå har vi egentlig lagd ulike modeller for to forskjellige systemer. Utvikling, testing og utforsking av modeller skal vi se mye på seinere. Her er hovedpoenget at du skal se hvordan løkker kan brukes til å løse problemer på en effektiv, robust og relativt intuitiv måte.

Nøstede løkker#

Nøstede løkker er løkker inni løkker. Da gjelder det å holde tunga rett i munnen. La oss bruke et eksempel for å forklare hvordan det virker:

print("i | j")
print("-----")
for i in range(2):
    for j in range(1,3):
        print(i,"|", j)
i | j
-----
0 | 1
0 | 2
1 | 1
1 | 2

Vi ser at den innerste løkka, som bruker j som tellevariabel, gjentas to ganger for hver gang den ytterste løkka går. Først er altså i = 0. Det er første runde i den ytre løkka. Mens i = 0 går den indre løkka to ganger, en gang for j = 1 og en gang for j = 2. Deretter går neste runde i den ytre løkka, og i = 1. Den indre løkka går igjen to ganger, en gang for j = 1 og en gang for j = 2. Totalt har vi altså gått fire løkkerunder.

Underveisoppgave

Lag et program som printer ut denne løkketabellen:

a

b

1

0

1

2

1

4

2

0

2

2

2

4

3

0

3

2

3

4

Oppgaver#

Oppgave 4.1

Sammenlikn programmene nedenfor. Beskriv eventuelle forskjeller og likheter.

partall = 0

for i in range(5):
    partall = partall + 2
    print(partall)
partall = 0

while partall < 10:
    partall = partall + 2
    print(partall)

Oppgave 4.2

Lag en løkketabell med utgangspunkt i følgende kode:

a = 0

for i in range(5):
    a = a + 1
    b = a*i

Oppgave 4.3

Lag et program som skriver ut “Du er flink til å programmere!” tusen ganger. Hvilken funksjon har “tellevariabelen” (i) her?

Oppgave 4.4

Programmet nedenfor skal finne summen av de 100 første tallene i en tallfølge der hvert ledd er den dobbelte av det forrige. Forklar hvordan programmet fungerer. Endre gjerne på ulike variabler og test hva utfallet blir for å forstå hvordan programmet fungerer.

Oppgave 4.5

Skriv om programmet ovenfor slik at du benytter en while-løkke istedenfor en for-løkke.

Oppgave 4.6

Hva skrives ut i følgende program? Prøv å undersøke dette for hånd. Til slutt kan du sjekke ved å kopiere programmet inn i en editor og kjøre det.

a = 0

for i in range(5):
    b = a*i
    print(b)
    a = a + 1
print(a)

Oppgave 4.7

Bruk turtle-grafikk til å tegne et hus. Du bestemmer selv hvor detaljert huset skal være.

Oppgave 4.8

Bruk turtle-grafikk til å tegne en hundrekant. Hva slags form minner dette deg om?

Oppgave 4.9

Programmet nedenfor skal regne ut hvor lang tid det tar før du har doblet beløpet ditt i banken gitt en årlig rente på 5 % og en startkapital på 5000 kroner, men programmet fungerer ikke som det skal. Hva er feil? Rett opp programmet slik at det fungerer.

Oppgave 4.10

Lag et program som regner ut denne summen: \(\sum_{n = 0}^{50} \frac{1}{2^n}\)

Oppgave 4.11

Lag et program som regner ut denne summen:

\(\sum_{n = 2}^{16} n^2 + n + 1\)

Oppgave 4.12

Fibonnacifølgen er en kjent tallfølge med heltall der hvert tall etter det første er summen av de to foregående. Følgen starter slik: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …

Lag et program som finner tall nr. n i rekka.

Oppgave 4.13

Lag et program som viser at summen av denne rekka er 4.5:

\(\displaystyle 3 + 1 + \frac{1}{3} + \frac{1}{9} + \frac{1}{27} + ...\)

Oppgave 4.14

I kombinatorikk er n-fakultet definert som produktet av alle heltall fra og med 1 til og med n. Dette kan vi skrive slik:

\(n! = \prod_{k=1}^{n} k\)

Lag en funksjon som regner ut fakultetet av et tall.

Oppgave 4.15

Vi kan tilnærme funksjonen \(f(x) = e^x\) med et Taylorpolynom av grad n slik:

\[e^x \approx \sum^\infty_{n=0} \frac{x^n}{n!} \approx 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!} + ...\]

a) Lag en funksjon exp_taylor som tar et punkt x og graden til polynomet n som parametere. Funksjonen skal returnere funksjonsverdien til Taylor-polynomet i punktet ved å benytte summeformelen ovenfor.

b) Lag en løkke som regner ut verdien til \(e^x\) for alle heltall i intervallet \(x \in [1,10]\) med funksjonen din. Regn også ut den “eksakte” verdien ved hjelp av exp-funksjonen til numpy. Skriv ut den absolutte feilen ved å ta differansen mellom den eksakte verdien og den estimerte.

En annen kjent funksjon vi kan tilnærme med Taylor-polynomer, er \(f(x) = cos(x)\):

\[cos(x) \approx \sum^\infty_{n=0}(-1)^n \frac{x^{2n}}{(2n)!} \approx 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \frac{x^8}{8!} + ...\]

c) Lag en funksjon cos_taylor som tar et punkt x og graden til polynomet n som parametere. Funksjonen skal returnere funksjonsverdien til Taylor-polynomet i punktet ved å benytte summeformelen ovenfor.

d) Lag en array x med 1000 likt fordelte verdier i intervallet \(x \in [-5,5]\) og plott \(f(x) = cos(x)\). Plott også en Taylor-polynomtilnærming med seks ledd i samme koordinatsystem. Bruk merkelapper (labels/legend) for å skille på kurvene.

e) Bruk funksjonen din til å plotte fire ulike tilnærminger til \(cos(x)\) ved hjelp av Taylor-polynomfunksjonen din i samme koordinatsystem som i d). De fire tilnærmingene skal ha grad fra 2–5, og merkelappene skal inneholde informasjon om graden til polynomet.

Oppgave 4.16

Vi kan bruke Bohrs formel til å regne ut frekvensen til et foton som emitteres når et elektron i et hydrogenatom deeksiterer fra skall \(n\) til \(m\):

\[f =\frac{B}{h}\cdot \left( \frac{1}{m^2} - \frac{1}{n^2} \right)\]

der \(B = 2.18\cdot 10^{-18} J\) er Bohrs konstant, \(h = 6.63\cdot 10^{-34}\) m\(^2\)kg s\(^{-1}\). Vi har også en sammenheng mellom frekvens og bølgelengden til fotonene:

\[\lambda = \frac{c}{f}\]

der \(c = 3.00\cdot10^8\) m/s er lysets hastighet i vakuum.

a) Lag et program som skriver ut bølgelengden ti lyset som emitteres ved en gitt deeksitasjon. Test programmet ved deeksitasjon fra \(n = 5\) til \(n = 2\). Dette skal gi \(\lambda = 434.47 \ nm\).

b) Lag et program som regner ut alle bølgelengdene til fotonene som emitteres når atomet deeksiterer fra et skall n til alle mulige energinivåer m.

Oppgave 4.17

Over tid vil den radioaktive strålingen i et radioaktivt stoff reduseres. Dette skjer fordi atomene i det radioaktive stoffet vil omdannes til andre grunnstoffer. Mengden \(N(t)\) som gjenstår av det radioaktive stoffet etter ei tid \(t\) kan finnes ved:

\(N(t) = N_0e^{-at}\)

der \(N_0\) er hvor mye radioaktivt stoff vi starter med ved tida \(t = 0\). Verdien til \(a\) forteller hvor raskt det radioaktive stoffet omdannes til andre grunnstoffer. Hvis vi kjenner halveringstida \(T\), vil \(a = \frac{\ln(2)}{T}\).

Plutonium-239 har halveringstid på omtrent \(T = 24\ 000\) år. Skriv et program som skriver ut hvor mye Plutonium-239 som gjenstår etter hvert 5000. år over en periode på 50 000 år dersom \(N_0 = 4\) kg.

Oppgave 4.18

Ideelle gasser kan beskrives med følgende likning:

\[PV = nRT\]

der \(P\) er trykket i pascal, \(V\) er volumet i m\(^3\), \(n\) er stoffmengden i mol, \(T\) er temperaturen i kelvin og \(R = 8.31\) m\(^2\)kgs\(^{-2}\)K\(^{-1}\)mol\(^{-1}\) er gasskonstanten. Vi ønsker å regne ut volumet gitt et sett med trykkverdier av 1 mol gass ved \(T = 300\) K.

a) Du skal lage en liste P med \(N\) likt fordelte verdier av \(P \in [100,400)\) kPa og en annen liste V med det tilsvarende volumet. Lag først en steglengde, steg, som sier noe om hvor langt det er mellom hver trykkverdi i intervallet. Husk at steglengden må være et heltall. Fyll så verdier i listene ved hjelp av en løkke.

b) Sjekk om listene er \(N\) lange ved å bruke len-funksjonen. Rett opp i programmet hvis de ikke er det.

c) Plott \(P\) som funksjon av \(V\).

Videoer#

I videoene nedenfor kan du få en innføring eller repetisjon i den grunnleggende teorien bak løkker:

Når du har sett videoen, kan du gjøre følgende oppgave for å sjekke om du forstår innholdet:

Underveisoppgave

Skriv et program som regner ut summen av alle heltallene fra og med 1 til og med 449 ved hjelp av en for-løkke.

Når du har sett videoen, kan du gjøre følgende oppgave for å sjekke om du forstår innholdet:

Underveisoppgave

Skriv et program som regner ut summen av tallene fra og med 1 til og med 449 ved hjelp av en while-løkke.