Uke 5

Generelt tips: les alltid gjennom kursnotatene før du begynner på lab’en!

Oppgave 1

Oppgave 1 består av fire deler. Skriv alle funksjoner til de fire delene (A-D) i én felles fil: uke_05_oppg_1.py

Del A

I filen uke_05_oppg_1.py, skriv en funksjon multiply_5_minus_pi(my_number) som:

Test koden din ved å legge til disse linjene nederst i uke_05_oppg_1.py filen:

from math import isclose
print("Tester multiply_5_minus__pi... ", end="")
assert isclose(1.86, multiply_5_minus_pi(1))
assert isclose(56.86, multiply_5_minus_pi(12))
assert isclose(611.86, multiply_5_minus_pi(123))
print("OK")

PS: Fra og med denne uken vil vi gi deg en såkalt ‘assert-test’ for hver oppgave (hvis det passer seg) som du kan bruke til å teste koden din og sikre at den fungerer som den skal.

En “assert” setning i Python brukes til å sjekke om en bestemt betingelse er sann (True). Hvis betingelsen er sann, vil programmet kjøre videre. Hvis betingelsen viser seg å være falsk (False), vil “assert” setningen utløse “AssertionError” og programmet vil stoppe.

Hvis alt er som det skal, vil assert testen i Oppgave 1 Del A skrive ut

Tester multiply_5_minus_pi... OK

IKKE ha med assert i innleveringen, det kan stoppe de automatiske testene!

Del B

Nå skal vi prøve et likt problem som oppgave 1, men denne tiden med typen str. Skriv en funksjon shout(text) som:

(Ikke print noe!)

Test koden din ved å legge til disse linjene nederst i uke_05_oppg_1.py filen:

print("Tester shout... ", end="")
assert "I love programming!" == shout("I love programming") 
assert "Adventure awaits!" == shout("Adventure awaits") 
assert "Excitement ahead!" == shout("Excitement ahead") 
print("OK")

Del C

Skriv en funksjon som heter name_age(name, gender, age):

Funksjonen skal ta tre argumenter:

Test koden din ved å legge til disse linjene nederst i uke_05_oppg_1.py filen:

print("Tester name_age... ", end="")
assert "Ola er kvinne og er 7 år gammel." == name_age("Ola", "kvinne", 7) 
assert "Sigurd er mann og er 20 år gammel." == name_age("Sigurd", "mann", 20) 
print("OK")

Del D

Skriv en funksjon som heter kinetic_energy(m,v). Funksjonen skal ta to argumenter \(m\) og \(v\) og skal returnere \( \frac{1}{2}mv^2\).

Test koden din ved å legge til disse linjene nederst i uke_05_oppg_1.py filen:

from math import isclose
print("Tester kinetic_energy... ", end="")
assert  isclose(4.0, kinetic_energy(2,2))
assert  isclose(128.0, kinetic_energy(4,8))
assert  isclose(2.5, kinetic_energy(5,1))
print("OK")
Oppgave 2

Del A

Begrepet \(pH\) er en forkortelse for potensialet til hydrogen. pH er en måleenhet som representerer konsentrasjonen av hydrogenioner i en løsning.

Om vi kaller konsentrasjonen for \([H⁺]\), beregnes pH-verdien med følgende ligning:

$$ pH = - log_{10}[H⁺]$$

og motsatt kan vi beregne \([H⁺]\) fra pH-verdien som:

$$ [H⁺] = 10^{- pH}$$

I filen uke_05_oppg_2.py skal du definere funksjonen conc_from_pH(pH) skal ta som input en float tall som representerer pH-verdien i en løsning og skal returnere konsentrasjonen av hydrogenioner til løsningen (vi antar at enhet er \(\frac{\text{mol}}{\text{L}} \)).

Test koden din ved å legge til disse linjene nederst i uke_05_oppg_2.py filen:

from math import isclose
print("Tester conc_from_pH... ", end="")
assert  isclose(0.1, conc_from_pH(1))
assert  isclose(1e-7, conc_from_pH(7))
assert  isclose(1e-10, conc_from_pH(10))
print("OK")

Del B

pH-skalaen beskriver surheten til løsningen: sur, nøytral eller basisk. En løsning med pH mindre enn 7 er en syre, nøyaktig 7 er en nøytral løsning og over 7 er en base.

Skriv funksjonen find_acidity(conc), og bør ta som input en konsentrasjons-verdi og returnere dens surhet som string: enten acidic, neutral eller basic.

print("Tester find_acidity... ", end="")
assert 'acidic' == find_acidity(0.1)
assert 'neutral'== find_acidity(1e-7)
assert 'basic' == find_acidity(1e-10)
print("OK")

  • Bruk \( pH = - log_{10}[H⁺]\)

  • Du kan importere log10 fra math-biblioteket

Oppgave 3

Tone ser etter drømmejobben, men har noen krav som skal oppfylles.

I filen uke_05_oppg_3.py, skrive funksjonen will_work(city, salary) som tar inn sted og lønn som parametere og returnerer True hvis Tone ville tatt jobben, False hvis ikke.

print("Tester will_work...", end="")
assert not will_work('Oslo', 949_999)
assert will_work('Kolvereid', 600_000) 
assert will_work('Bergen', 400_000) 
assert not will_work('Bergen', 399_999) 
assert not will_work('Kristiansand', 590_000) 
assert will_work('Oslo', 950_000)  
assert will_work('Verdensrommet', 10) 
print("OK")
Oppgave 4

Del A

I filen uke_05_oppg_4.py skriv en funksjon cross_sum med en parameter n som returnerer tverrsummen av et gitt tall. Tverrsummen er summen av sifrene i tallet, for eksempel er tverrsummen av 12 lik 3, siden 1 + 2 = 3.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester cross_sum... ", end="")
assert 1 == cross_sum(1) 
assert 3 == cross_sum(12) 
assert 6 == cross_sum(123) 
assert 10 == cross_sum(1234) 
assert 10 == cross_sum(4321) 
print("OK")

  • Du kan isolere det siste sifferet i et tall n med modulo-operasjonen n % 10 (for eksempel evaluerer 123 % 10 til 3).

  • Du kan fjerne det siste sifferet i et tall n med heltallsdivisjon n // 10 (for eksempel evaluerer 123 // 10 til 12).

  • Begynn med å opprette en variabel for tverrsummen, som i utgangsposisjon (før løkken starter) har verdien 0. Planen er å la dette være en løpende total, altså en variabel der vi adderer inn nye verdier flere ganger.

  • Så lenge tallet n er større enn 0: isoler det siste sifferet og legg det til i tverrsummen. Fjern så det siste sifferet fra n og fortsett med en ny iterasjon.

  • Det finnes også en løsning med str() som er veldig pent.

Del B

I filen uke_05_oppg_4.py skriv en funksjon nth_number_with_cross_sum_x(n, x) som returnerer det n’te tallet hvor tverrsummen av tallet er x.

Det første tallet med tverrsum 7 er bare tallet 7, mens det andre tallet med tverrsum 7 er 16. Derfor skal nth_number_with_cross_sum_x(1, 7) returnere 7, mens nth_number_with_cross_sum_x(2, 7) skal returnere 16.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester nth_number_with_cross_sum_x... ", end="")
assert 7 == nth_number_with_cross_sum_x(1, 7) 
assert 16 == nth_number_with_cross_sum_x(2, 7)
assert 25 == nth_number_with_cross_sum_x(3, 7) 
assert 19 == nth_number_with_cross_sum_x(1, 10) 
assert 28 == nth_number_with_cross_sum_x(2, 10)
assert 37 == nth_number_with_cross_sum_x(3, 10) 
assert 2000 == nth_number_with_cross_sum_x(10, 2) 
print("OK")

Merk: i denne oppgaven vet du ikke før løkken starter hvor mange iterasjoner løkken skal ha. Derfor bør du velge en while-løkke, og ikke en for-løkke.

Bonus 1

Opprett en funksjon is_even_positive_int som har en parameter x. Funksjonen skal returnere True hvis x er et positivt partall av typen int, og False ellers.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester is_even_positive_int...", end="")
assert is_even_positive_int(123456) # True, dette er et positivt partall
assert not is_even_positive_int(-2)  # False (er ikke positivt)
assert not is_even_positive_int(123)  # False (er ikke et partall)
assert not is_even_positive_int("huffda")  # False (er ikke en int)
print("OK")

  • For å sjekke om en variabel x er av typen int, benytt type(x) == int.

  • Benytt % -operatøren for å avgjøre om et tall er partall eller oddetall.

Bonus 2

Skriv en funksjon is_legal_triangle som har tre parametre. La funksjonen returnere True dersom de tre parametrene representerer postive tall som kan representere lengden på sidene i en trekant,1 og False hvis en slik trekant ikke kan eksistere. Merk fra trekantulikheten at summen av ethvert valg av to sider alltid er større enn den tredje siden.

Funksjonen skal fungere dersom argumentene er enten float eller int; dersom et argument har en annen type skal funksjonen returnere False, og ikke krasje.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester is_legal_triangle... ", end="")
assert is_legal_triangle(2, 2, 3)  # True, dette er en mulig trekant
assert not is_legal_triangle(3, 2, 1)  # False (1 + 2 er ikke større enn 3)
assert not is_legal_triangle("2", "2", "3")  # False (dette er ikke tall)
print("OK")

  • Begynn med å sjekke at argumentene har riktig type. For hver parameter, sjekk om typen er int eller float, og hvis ikke returner False.

  • For hver sidelengde, sjekk at summen av de to andre sidelengdene er større. Om du finner en sidelengde som er like lang eller lengre enn summen av de to andre, returner False.

  • Til slutt returnerer du bare True

Bonus 3

I denne oppgaven skal vi skrive en kunstig intelligens for å spille Joker fra Norsk Tipping. I dette spillet får man på forhånd vite fem hovedtall; for hvert av hovedtallene må man velge om man skal gå “opp” eller “ned” før et hemmelig vippetall avsløres. Dersom man valgte “opp” og det avslørte vippetallet er høyere eller lik hovedtallet, vinner man en større premie. Det samme skjer dersom man velger “ned” og vippetallet er lavere eller lik hovedtallet. Vi antar at de hemmelige vippetallene er tilfeldig valgt mellom 0 og 9.

Strategien vi skal implementere er å si “opp” dersom hovedtallet er 4 eller lavere, og si “ned” ellers.

Skriv en funksjon joker(x1, x2, x3, x4, x5) som har fem parametre (x1, x2, x3, x4 og x5) som representerer hovedtallene vi får oppgitt når vi begynner å spille Joker. Funksjonen skal skrive ut fem linjer, hvor hver enkelt linje gir oss strategien, enten “opp” eller “ned”, for hvert av de fem hovedtallene. For eksempel skal et kall

joker(3, 4, 5, 6, 1)

skrive ut følgende til terminalen:

opp
opp
ned
ned
opp

Merk: denne funksjonen behøver ikke returnere noen verdi.

Bonus 4

Del A

Et hyperrektangel er et rektangel hvor sidene er vannrette og loddrette (ikke rotert). Vi kan representere et hyperrektangel med to koordinater \((x_0, y_0)\) og \((x_1, y_1)\) som representerer to diagonalt motsatte hjørner. I denne oppgaven skal du avgjøre hvorvidt et punkt befinner seg innenfor et slikt rektangel eller ikke.

Skriv en funksjon point_in_rectangle som har seks parametre x0, y0, x1, y1, x2, y2, hvor de fire første parametrene representerer et hyperrektangel, og de to siste representerer et vilkårlig punkt. La metoden returnere True dersom punktet er innenfor rektangelet, og False hvis ikke. Dersom punktet befinner seg akkurat på linjen, regner vi det som at den er innenfor.

Du skal ikke importere noen biblioteker for å løse denne oppgaven.

Illustrasjon av rektangel og punktIllustrasjon testcaser for 8a

Test koden din:

print("Tester point_in_rectangle... ", end="")
assert point_in_rectangle(0, 0, 5, 5, 3, 3)  # Midt i
assert point_in_rectangle(0, 5, 5, 0, 5, 3) # På kanten
assert not point_in_rectangle(0, 0, 5, 5, 6, 3)  # Utenfor
print("OK")

  • Omregn rektangelet slik at du vet hva som er høyre og venstre, top og bunn. For eksempel, opprett variabler x_left = min(x0, x1) og x_right = max(x0, x1). Tilsvarende for topp og bunn med y-aksen.

  • Sjekk at punktet \((x_2, y_2)\) både befinner seg mellom venstre- og høyresiden, og også mellom topp og bunn.

  • For eksempel: punktet \((x_2, y_2)\) er mellom høyre- og venstre siden dersom både x_left er mindre eller lik x2 og x_right er større enn eller lik x_2.

Del B

Skriv en funksjon rectangles_overlap som har åtte parametre x0, y0, x1, y1, x2, y2, x3, y3, hvor de fire første parametrene representerer ett hyperrektangel, og de fire siste representerer et annet. La metoden returnere True dersom rektanglene overlapper hverandre, og False hvis ikke. Vi sier at rektanglene overlapper selv om de kun deler ett enkelt punkt.

Du skal ikke importere noen biblioteker for å løse denne oppgaven.

Illustrasjon av overlappende rektangler

Test koden din:

print("Tester rectangles_overlap... ", end="")
assert rectangles_overlap(0, 0, 5, 5, 2, 2, 6, 6)  # Delvis overlapp
assert rectangles_overlap(0, 5, 5, 0, 1, 1, 4, 4)  # Fullstendig overlapp
assert rectangles_overlap(0, 1, 7, 2, 1, 0, 2, 7)  # Kryssende rektangler
assert rectangles_overlap(0, 5, 5, 0, 5, 5, 7, 7)  # Deler et hjørne
assert not rectangles_overlap(0, 0, 5, 5, 3, 6, 5, 8)  # Utenfor
print("OK")

  • Omregn begge rektangler slik at du vet hva som er høyre og venstre, top og bunn (se hint for 8a).
  • Dersom høyresiden til ett rektangel er til venstre for venstresiden av det andre, blir svaret false (tilsvarende logikk med topp og bunn). Husk å sjekke begge retninger.

Illustrasjon av testcasene over: Illustrasjon av testcasene for 8b

Del C

Skriv en funksjon circle_overlaps_rectangle som har syv parametre x0, y0, x1, y1, x2, y2, r2, hvor de fire første parametrene representerer et hyperrektangel, og de tre siste representerer en sirkel sentrert i \((x_2, y_2)\) med radius \(r_2\). La metoden returnere True dersom sirkelen overlapper rektangelet, og False hvis ikke. Dersom sirkelen og rektangelet deler kun ett enkelt punkt regnes det fremdeles som at de er overlappende.

Du skal ikke importere noen biblioteker for å løse denne oppgaven.

Illustrasjon av sirkel og rektangel som ikke overlapper

Test koden din:

print("Tester circle_overlaps_rectangle... ", end="")
assert circle_overlaps_rectangle(0, 0, 5, 5, 2.5, 2.5, 2)  # sirkel i midten
assert not circle_overlaps_rectangle(0, 5, 5, 0, 8, 3, 2)  # langt utenfor
assert circle_overlaps_rectangle(0, 0, 5, 5, 2.5, 7, 2.01)  # langs kanten
assert circle_overlaps_rectangle(0, 5, 5, 0, 5.1, 5.1, 1)  # på hjørnet
assert circle_overlaps_rectangle(0, 0, 5, 5, 8, 8.99, 5)  # på hjørnet
assert not circle_overlaps_rectangle(0, 0, 5, 5, 8, 9.01, 5)  # akkurat ikke
print("OK")

  • Dersom sirkelens sentrum er inne i rektangelet, er svaret True. Bruk funksjonen du skrev i 8a for å sjekke dette. Du kan kalle på funksjonen, du trenger ikke å skrive logikken på nytt. For eksempel: if point_in_rectangle(...):.
  • Konverter punktene fra x0 og x1 til venstrekant (minimum av x0 og x1) og høyrekant (maksimum) (se også hint til 8a). På samme måte, omgjør punktene y0 og y1 til topp og bunn.
  • “Utvid” rektangelet med sirkelens radius i alle retninger. Hvis sirkelens sentrum er utenfor dette utvidede rektangelet (bruk funksjonen fra 8a igjen), er det garantert ikke noe overlapp.
  • I de gjenstående tilfellene befinner sirkelen sitt sentrum seg i rammen rundt rektangelet (se figur over).
    • Dersom sirkelens x-koodinat befinner seg mellom x-koordinatene til det opprinnelige rektangelet, er det overlapp.
    • Tilsvarende for y-aksen.
    • Hvis sirkelens sentrum ikke tilfredsstiller noen av de to punktene over, befinner det seg i et av hjørnene. Dersom sirkelens sentrum har større avstand enn \(r_2\) til samtlige hjørner i det opprinnelige rektangelet, er det ingen overlapp (f. eks. slik som på figuren over). For å finne avstanden, bruk funksjonen fra lab1.

Illustrasjon av testcasene oppgitt over (en sirkel per testcase):

Illustrasjon av testcasene for 8c

Flytdiagram for circle_overlaps_rectangle

if point_in_rectangle(...):  # orginal rektangel
    return True
elif not point_in_rectange(...): # utvidet rektangel
    return False
elif ... # punkt er mellom venstre og høyresiden til rektangel (x-aksen)
    return True
elif ... # punkt er mellom topp og bunn til rektangel (y-aksen)
    return True
elif distance(...) # sirkelen overlapper hjørnet oppe til venstre
    return True
elif ... # sirkelen overlapper hjørnet oppe til høyre
    return True
...


  1. Vår definisjon på en trekant tillater ikke “tomme” trekanter uten areal. ↩︎