E-tenta 2023-10-26#

Information#

Tentamen består av två delar, del A och del B.

  • För betyg 3 krävs 6/10 på del A.

  • För betyg 4 krävs betyg 3 samt 4/12 på del B.

  • För betyg 5 krävs betyg 3 samt 8/12 på del B.

Del B rättas enbart om del A är godkänd.

Hjälpmedel#

Via den dator du skriver tentamen på har du åtkomst till:

Länkar till dessa hjälpmedel finns som flikar längst ned i Inspera.

Eftersom du har tillgång till en programmeringsmiljö kan du testa din kod innan du lämnar in den i Inspera. Du kopierar ditt svar från Programiz/Online Python/GDB Online till Inspera. Markera koden med CTRL-A, kopiera med CTRL-C och klistra in i svarsrutan i Inspera med CTRL-V. Ångra med CTRL-Z.

Medan du skriver tentamen sparas dina svar i Inspera var femtonde sekund. Du kan inte öppna tentan igen när du har lämnat in den.

Bedömning#

I del A krävs genomgående att din kod ska fungera enligt anvisning. Kod som inte fungerar ges endast i undantagsfall något annat än 0 poäng.

I del B kan du få delpoäng för icke-fungerande kod som ändå visar att du kommit en bit på vägen.

Kommentering av kod behövs ej för del A. Uppgifterna på del B bör kommenteras på de ställen där en kommentar ökar läsbarheten markant. Svenska kommentarer är ok.

Såvida inget annat anges får du bygga på lösningar till föregående uppgifter även om du inte har löst dessa. Det är tillåtet att införa extra metoder eller funktioner. Uttryck som ”skriv en funktion som” ska alltså inte tolkas så att lösningen inte får struktureras med hjälp av fler funktioner. Alla uppgifter gäller programmeringsspråket Python och all programkod ska skrivas i Python. Koden ska vara läslig, dvs. den ska vara vettigt strukturerad och indenterad. Namn på variabler, funktioner, metoder, klasser etc. ska vara beskrivande men kan ändå hållas ganska korta.

A1#

Nedan är innehållet i en funktion. Den första raden (”funktionshuvudet”) i funktionsdefinitionen har dock försvunnit. Hur bör den se ut?

    c = (a**2 + b**2)**0.5
    return c

Alternativ 1: def hypotenuse():

Alternativ 2: def hypotenuse(a):

Alternativ 3: def hypotenuse(a, b):

Alternativ 4: def hypotenuse(a, b, c):

Alternativ 5: def rectangle_area():

Alternativ 6: def rectangle_area(a):

Alternativ 7: def rectangle_area(a, b):

Alternativ 8: def rectangle_area(a, b, c):

Svar

Alternativ 3.

A2#

Givet en tupel (tpl = (1,2,3)) och en lista (lst = [1, 5, 8]), vilken av följande satser kommer orsaka ett felmeddelande?

Alternativ 1: tpl.append(4)

Alternativ 2: lst.sort()

Alternativ 3: tpl.index(3)

Alternativ 4: lst.append(4)

Svar

Alternativ 1

A3#

När koden nedan körs skrivs True ut. Hur kan det komma sig? Välj den motivering som stämmer bäst.

x = [5, 4, 3]
y = x
x.append(1)
print(x[-1] == y[-1])

Alternativ 1: Variablerna x och y refererar till samma lista. Det sista elementet i x är därför likadant som det sista elementet i y.

Alternativ 2: De första elementen i x och y är likadana och därför skrivs True ut.

Alternativ 3: Operatorn == innebär tilldelning. Efter en lyckad tilldelning som inte ger något felmeddelande returneras True.

Alternativ 4: Den andra raden i programmet stipulerar att variablerna x och y ska ha samma värde. Det går därför inte att lägga till element i listan x med metoden append(), så den tredje raden kommer inte att få någon effekt.

Svar

Alternativ 1

A4#

Någon har skrivit en klass MyTurtle som ska vara en mycket enkel variant av klassen turtle.Turtle.

import math
 
class MyTurtle:
 
    def __str__(self):
        return f'Turtle at position ({self.x:2.1f}, {self.y:2.1f}), with heading {self.heading}.'
 
    def forward(self, dist):
        """Move the turtle a distance of dist units in the current heading"""
        heading_in_radians = self.heading*math.pi/180
        self.x = self.x + dist*math.cos(heading_in_radians)
        self.y = self.y + dist*math.sin(heading_in_radians)

Som du ser har klassens konstruktor försvunnit! Hur bör den se ut? Välj ett av alternativen nedan.

Alternativ 1

    def __init__(self):
        import random
        x = 0
        y = 0
        heading = random.randint(0, 359)

Alternativ 2

    def __init__(self):
        x = float(input('x coordinate: '))
        y = float(input('y coordinate: '))
        heading = int(input('heading: '))

Alternativ 3

    def __init__(self, x, y):
        self.pos = (x, y)
        self.heading = 0

Alternativ 4

    def __init__(self, x, y):
        self.x = x
        self.y = y
        heading = 'North'

Alternativ 5

    def __init__(self, x, y, heading):
        self.x = x
        self.y = y
        self.heading = heading

Alternativ 6

    def __init__(self, x, y, heading):
        self.x = x
        self.y = y
        heading = 0
Svar

Alternativ 5

A5: Resistors#

Ersättningsresistansen \(R\) för \(n\) parallelkopplade resistanser \(R_1, R_2, \ldots, R_n\) uppfyller sambandet

\[\frac{1}{R} = \frac{1}{R_1} + \frac{1}{R_2} + \cdots + \frac{1}{R_n}.\]

Skriv en funktion resistance som tar en lista med de \(n\) resistanserna som parameter och returnerar ersättningsresistansen \(R\).

Följande anrop:

rList1 = [3000, 3000, 3000]
rList2 = [1000, 2000, 3000]
print(resistance(rList1))
print(resistance(rList2))

ska ge utskriften

1000.0
545.4545454545455
Lösningsförslag
def resistance(r_list):
    inverted_r_list = [1/r for r in r_list]
    return 1/sum(inverted_r_list)

A6: Every other uppercase#

Skriv en funktion every_other_uppercase(word) som tar en sträng som representerar ett ord som parameter. Funktionen ska returnera en likadan sträng, med skillnaden att den första bokstaven och varannan efterföljande bokstav är skriven med versal (stor bokstav).

Du kan anta att ordet består av enbart bokstäver, dvs. strängen word innehåller inga tecken som inte är bokstäver. Bokstäver som redan är versaler ska ej förändras.

Följande kod:

print(every_other_uppercase('programming'))
print(every_other_uppercase('Python'))
print(every_other_uppercase('PYTHON'))

ska ge utskriften

PrOgRaMmInG
PyThOn
PYTHON
Lösningsförslag
def every_other_uppercase(word):
    result = ''
    for i in range(len(word)):
        if i % 2 == 0:
            result += word[i].upper()
        else:
            result += word[i]
    return result

A7: Convert cm to m#

I en viss databas finns listor som innehåller längder i enheten cm. Längerna är sparade som strängar. Skriv en funktion convert_cm_to_m(lengths_in_cm) som tar en sådan lista med centimetervärden som parameter. Funktionen ska returnera en lista med längderna konverterade till enheten m. Längderna ska fortfarande vara strängar!

Följande kod:

heights_in_cm = ['165', '161', '183', '195']
heights_in_m = convert_cm_to_m(heights_in_cm)
for h_cm, h_m in zip(heights_in_cm, heights_in_m):
    print(h_cm + ' cm = ' + h_m + ' m.')

ska ge utskriften

165 cm = 1.65 m.
161 cm = 1.61 m.
183 cm = 1.83 m.
195 cm = 1.95 m.
Lösningsförslag
def convert_cm_to_m(lengths_in_cm):
    lengths_in_m = []
    for l in lengths_in_cm:
        lengths_in_m.append(str(float(l)/100))
    return lengths_in_m

A8: Sum with limit#

Skriv en funktion sum_with_limit(numbers, limit) som tar en lista med tal (numbers) och en övre gräns (limit) som parametrar. Funktionen ska summera tal i listan tills summan blir större än eller lika med limit. När detta sker ska funktionen returnera summan och antalet termer som summerats. Om limit aldrig nås ska funktionen returnera summan av alla tal i listan samt antalet element i listan.

Följande kod

x = [2, 5, 1, 4, 7, 3]
sum, num_elements = sum_with_limit(x, 7)
print(f'The sum of the first {num_elements} elements in x is {sum}.')

sum, num_elements = sum_with_limit(x, 10)
print(f'The sum of the first {num_elements} elements in x is {sum}.')

sum, num_elements = sum_with_limit(x, 30)
print(f'The sum of the first {num_elements} elements in x is {sum}.')

ska ge utskriften

The sum of the first 2 elements in x is 7.
The sum of the first 4 elements in x is 12.
The sum of the first 6 elements in x is 22.
Lösningsförslag
def sum_with_limit(numbers, limit):
    i = 0
    sum = 0
    while sum < limit and i < len(numbers):
        sum += numbers[i]
        i += 1
    return sum, i

A9: Tall players#

Basketlaget Uppsala Hoops letar efter en ny center. De söker i första hand en spelare som är minst 2 m lång. De har tillgång till ett lexikon med tillgängliga spelare, där spelarnas namn är nycklar och deras längder (i enhet m) är värden. I körexemplet nedan visas hur ett sådant lexikon kan se ut. Skriv en funktion extract_people_taller_than(name_to_height, limit) som tar ett sådant lexikon (name_to_height) och den kortaste längd man kan acceptera (limit) som parametrar. Funktionen ska returnera en lista med namnen på de spelare som är minst limit m långa.

Följande kod:

name_to_height = {'Player 1': 2.00,
'Player 2': 1.96,
'Player 3': 2.12,
'Player 4': 1.95}
tall_players = extract_people_taller_than(name_to_height, 2.0)
print('The following players are at least 2 m tall:')
for player in tall_players:
    print(player)

ska ge utskriften

The following players are at least 2 m tall:
Player 1
Player 3

Det är inte viktigt att spelarnas namn skrivs ut i samma ordning som i exemplet. Din funktion ska fungera även för andra lexikon med samma struktur.

Lösningsförslag
def extract_people_taller_than(name_to_height, limit):
    names = []
    for name, height in name_to_height.items():
        if height >= limit:
            names.append(name)
    return names

A10: draw_triangle#

Skriv en funktion draw_triangle(n), som tar ett positivt heltal n som parameter och skriver ut en ”triangel av storlek n” enligt följande exempel:

draw_triangle(5)
print()
draw_triangle(9)

ska ge utskriften

0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
 
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
Lösningsförslag
def draw_triangle(n):
    for i in range(n):
        for j in range(i+1):
            print(j, end=' ')
        print()

B1-B3: Polynom#

Nedan finns en klass Polynomial definierad. Den innehåller en konstruktor och en __str__-metod. Dessa metoder är färdiga och du kommer inte behöva ändra dem. Kopiera koden till en editor och läs sedan instruktionerna som kommer efter koden.

class Polynomial:
    def __init__(self, coeffs=[]):
        self.coeffs = coeffs

    def __str__(self):
        deg = len(self.coeffs) - 1
        if deg == -1:
            # Empty polynomial, treated as 0.
            return 'p(x) = 0'
        elif deg == 0:
            # Constant (zeroth degree polynomial)
            return f'p(x) = {self.coeffs[0]}'
        else:
            # Polynomial of degree >= 1
            result = 'p(x) ='
            power = deg
            for coeff in self.coeffs:
                if coeff != 0:
                    if power == 0:
                        term = f'{abs(coeff)}'
                    elif abs(coeff) == 1:
                        term = f'x^{power}'
                    else:
                        term = f'{abs(coeff)}x^{power}'
                    if power == deg and coeff > 0:
                        sign_str = ' '
                    else:
                        sign_str = ' - ' if coeff < 0 else ' + '
                    result += sign_str + term
                power -= 1
        return result

Objekt av klassen Polynomial representerar polynom. Matematiskt kan ett polynom av grad \(n\) skrivas

\[ p(x) = a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1 x + a_0. \]

Ett polynom är alltså entydigt bestämt av koefficienterna \(a_0, a_1, \ldots, a_n\).

Följande är viktig information om klassen Polynomial:

  1. Konstruktorn och __str__-metoden är färdiga och du ska inte ändra dem. Du behöver läsa konstruktorn och förstå hur den fungerar. Du behöver däremot inte läsa koden i __str__-metoden för att kunna lösa uppgifterna. Du rekommenderas dock att utnyttja metoden för att kontrollera att du gjort rätt.

  2. Klassen Polynomial har en instansvariabel, coeffs, som är en lista med koefficienterna \(a_n, a_{n-1}, \ldots, a_0\). Notera att \(a_n\) är det första elementet i coeffs och \(a_0\) är det sista.

  3. För att klassen ska fungera korrekt får det första elementet i coeffs (som svarar mot \(a_n\)) inte vara 0. Tack vare att vi kan anta att det första elementet ej är 0 kan polynomets grad beräknas som len(self.coeffs) - 1.

  4. Nollpolynomet \(p(x)=0\) är ett specialfall som representeras med en tom lista: coeffs = []. (Eftersom det första elementet i coeffs inte får vara 0 kan vi inte använda t.ex. coeffs = [0].)

Uppgift B1 (4p)#

Lägg till en metod evaluate i klassen Polynomial. Metoden ska ta ett tal \(x_0\) som parameter och returnera talet \(p(x_0)\). Detta kallas för att utvärdera polynomet \(p\) i punkten \(x_0\). Matematiskt gäller:

\[ p(x_0) = a_n x_0^n + a_{n-1}x_0^{n-1} + \cdots + a_1 x_0 + a_0. \]
Lösningsförslag
    def evaluate(self, x):
        res = 0
        deg = len(self.coeffs) - 1
        power = deg
        for c in self.coeffs:
            res += c * x**power
            power -= 1
        return res

Uppgift B2 (4p)#

Skriv en funktion polynomial_derivative(p) som tar ett objekt av klassen Polynomial som parameter. Funktionen ska returnera ett nytt Polynomial-objekt som motsvarar derivatan av det första polynomet.

Kom ihåg att derivatan av ett polynom beräknas enligt:

\[ p'(x) = n a_n x^{n-1} + (n-1) a_{n-1}x^{n-2} + \cdots + 2a_2 x + a_1 . \]

Notera att polynomial_derivative ska vara en funktion, inte en metod i klassen Polynomial.

Tips: För att slutresultatet ska bli helt rätt, kom ihåg att nollpolynomet \(p(x)=0\) ska representeras med koefficientlistan coeffs = [].

Lösningsförslag
def polynomial_derivative(p):
    deg = len(p.coeffs) - 1
    if deg <= 0:
        return Polynomial([])
    derivative_coeffs = p.coeffs[:-1]
    power = deg
    for i in range(len(derivative_coeffs)):
        derivative_coeffs[i] = derivative_coeffs[i] * power
        power -= 1
    return Polynomial(derivative_coeffs)

Uppgift B3 (4p)#

Skriv en funktion add_polynomials(p1, p2). Funktionen ska ta två objekt av klassen Polynomial som parametrar och returnera ett Polynomial-objekt som motsvarar summan av de två första polynomen. Du kan inte anta att p1 och p2 är polynom av samma grad.

Betrakta två polynom \(p_1\) och \(p_2\):

\[ p_1(x) = a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1 x + a_0. \]
\[ p_2(x) = b_m x^m + b_{m-1}x^{m-1} + \cdots + b_1 x + b_0. \]

Antag att \(m>n\). Summan av \(p_1\) och \(p_2\) kan då skrivas som

\[\begin{split} \begin{aligned} p_1(x) + p_2(x) &= b_m x^m + b_{m-1} x^{m-1} + \cdots + b_{n+1} x^{n+1} + \\ &+ (a_n + b_n)x^n + (a_{n-1} + b_{n-1})x^{n-1} + \cdots + (a_1 + b_1)x + a_0 + b_0 \end{aligned} \end{split}\]

Notera att add_polynomials ska vara en funktion, inte en metod i klassen Polynomial.

Tips: För att slutresultatet ska bli helt rätt, kom ihåg att se till att det polynom du returnerar inte har 0 som första koefficient.

Lösningsförslag
def add_polynomials(p1, p2):
    c1 = p1.coeffs
    c2 = p2.coeffs
    deg1 = len(c1) - 1
    deg2 = len(c2) - 1

    if deg1 > deg2:
        min_deg = deg2
        coeffs = c1.copy()
        coeffs_to_add = c2
    else:
        min_deg = deg1
        coeffs = c2.copy()
        coeffs_to_add = c1

    for i in range(1, min_deg+2):
        coeffs[-i] += coeffs_to_add[-i]

    while len(coeffs) > 0 and coeffs[0] == 0:
        coeffs.pop(0)

    return Polynomial(coeffs)