Løsningsforslag — IN1900 midtveis - programmeringsoppgaver
Dette er én måte å løse oppgavene på, ikke den eneste. Koden er kjørt, og utskriften under er den faktiske.
Oppgave 1
# a) while-løkke
print(f"{'F':>6s}{'C':>8s}{'C_ca':>8s}")
F = 0
while F <= 100:
C = 5/9*(F - 32)
C_ca = (F - 30)/2
print(f"{F:6d}{C:8.2f}{C_ca:8.1f}")
F += 10
# b) for-løkke, lister, nøstet liste
F_list = []; C_list = []; Cca_list = []
for F in range(0, 101, 10):
F_list.append(F)
C_list.append(5/9*(F - 32))
Cca_list.append((F - 30)/2)
tabell = [[f, c, ca] for f, c, ca in zip(F_list, C_list, Cca_list)]
print("\nRader der |C - C_ca| < 2:")
for rad in tabell:
if abs(rad[1] - rad[2]) < 2:
print(f"{rad[0]:6d}{rad[1]:8.2f}{rad[2]:8.1f}")
# c) største feil med løkke
maks_feil = 0; F_maks = None
for f, c, ca in tabell:
feil = abs(c - ca)
if feil > maks_feil:
maks_feil = feil; F_maks = f
print(f"\nStørst feil {maks_feil:.2f} grader ved F = {F_maks}")
# d) slicing
print(tabell[:3])
print(tabell[-2:])Utskrift:
F C C_ca
0 -17.78 -15.0
10 -12.22 -10.0
20 -6.67 -5.0
30 -1.11 0.0
40 4.44 5.0
50 10.00 10.0
60 15.56 15.0
70 21.11 20.0
80 26.67 25.0
90 32.22 30.0
100 37.78 35.0
Rader der |C - C_ca| < 2:
20 -6.67 -5.0
30 -1.11 0.0
40 4.44 5.0
50 10.00 10.0
60 15.56 15.0
70 21.11 20.0
80 26.67 25.0
Størst feil 2.78 grader ved F = 0
[[0, -17.77777777777778, -15.0], [10, -12.222222222222223, -10.0], [20, -6.666666666666667, -5.0]]
[[90, 32.22222222222222, 30.0], [100, 37.77777777777778, 35.0]]
Tilnærmingen er best rundt 50 °F og bommer mest i endene av tabellen.
Oppgave 2
import sys
from math import sqrt
def tyngdeakselerasjon(M, R, G=6.674e-11):
if R <= 0:
raise ValueError(f"Radius må være positiv, fikk R = {R}")
return G*M/R**2
def test_tyngdeakselerasjon():
expected = 2.0
computed = tyngdeakselerasjon(8.0, 2.0, G=1.0)
tol = 1e-12
success = abs(expected - computed) < tol
msg = f"Forventet {expected}, fikk {computed}"
assert success, msg
def les_planeter(filnavn):
planeter = {}
with open(filnavn) as infile:
infile.readline()
for line in infile:
w = line.split()
try:
masse = float(w[1])*1e24 # kg
radius = float(w[2])*1e3 # m
except ValueError:
print(f"Hopper over linja for {w[0]}: kunne ikke lese tallene")
continue
planeter[w[0]] = (masse, radius)
return planeter
test_tyngdeakselerasjon()
try:
tyngdeakselerasjon(1.0, 0)
except ValueError as e:
print(f"Feil: {e}")
try:
filnavn = sys.argv[1]
except IndexError:
print("Bruk: python oppg2.py <filnavn>")
sys.exit(1)
planeter = les_planeter(filnavn)
g_verdier = {}
for navn in planeter:
M, R = planeter[navn]
g_verdier[navn] = tyngdeakselerasjon(M, R)
print(f"{'Planet':10s}{'g (m/s^2)':>10s}")
for navn in g_verdier:
print(f"{navn:10s}{g_verdier[navn]:10.2f}")
sterkest = None
for navn in g_verdier:
if sterkest is None or g_verdier[navn] > g_verdier[sterkest]:
sterkest = navn
print(f"Sterkest tyngdekraft: {sterkest}")
h = 10.0
for navn in g_verdier:
t = sqrt(2*h/g_verdier[navn])
print(f"{navn:10s} fall fra {h} m tar {t:.2f} s")
with open('tyngdekraft.txt', 'w') as outfile:
outfile.write("# planet g\n")
for navn in g_verdier:
outfile.write(f"{navn} {g_verdier[navn]:.3f}\n")Utskrift med python planeter.py planeter.txt:
Feil: Radius må være positiv, fikk R = 0
Hopper over linja for Pluto: kunne ikke lese tallene
Planet g (m/s^2)
Merkur 3.70
Venus 8.87
Jorden 9.82
Mars 3.73
Jupiter 25.92
Sterkest tyngdekraft: Jupiter
Merkur fall fra 10.0 m tar 2.33 s
Venus fall fra 10.0 m tar 1.50 s
Jorden fall fra 10.0 m tar 1.43 s
Mars fall fra 10.0 m tar 2.32 s
Jupiter fall fra 10.0 m tar 0.88 s
Uten filnavn gir sys.argv[1] en IndexError, som fanges og gir bruksanvisningen.
Oppgave 3
import numpy as np
import matplotlib.pyplot as plt
from math import factorial
def S(x, n):
s = 0
for k in range(n + 1):
s += (-1)**k * x**(2*k + 1) / factorial(2*k + 1)
return s
def test_S():
tol = 1e-12
assert abs(S(0.0, 5)) < tol, "S(0, n) skal være 0"
assert abs(S(0.3, 0) - 0.3) < tol, "S(x, 0) skal være x"
expected = 1 - 1/6
assert abs(S(1.0, 1) - expected) < tol, f"S(1,1) skal være {expected}"
test_S()
# b) løkke vs vektorisert
N = 201
x_loop = np.zeros(N); y_loop = np.zeros(N)
dx = 2*np.pi/(N - 1)
for i in range(N):
x_loop[i] = i*dx
y_loop[i] = S(x_loop[i], 3)
x = np.linspace(0, 2*np.pi, N)
y = S(x, 3)
print("like:", np.allclose(x, x_loop), np.allclose(y, y_loop))
# c) plot
plt.plot(x, np.sin(x), 'k-', label='sin(x)')
for n in [1, 3, 5]:
plt.plot(x, S(x, n), label=f'S(x, {n})')
plt.xlabel('x'); plt.ylabel('y'); plt.ylim(-2, 2); plt.legend()
plt.show()
# d) funksjoner som argument
def maks_feil(f, g, x):
return np.max(np.abs(f(x) - g(x)))
for n in range(1, 7):
feil = maks_feil(np.sin, lambda x: S(x, n), x)
print(f"n = {n}: maks feil {feil:.2e}")
n = 0
while maks_feil(np.sin, lambda x: S(x, n), x) >= 1e-3:
n += 1
print("minste n:", n)
# e) slicing
halv = x[:len(x)//2 + 1]
print(f"[0, pi]: {maks_feil(np.sin, lambda x: S(x, 3), halv):.2e}, [0, 2pi]: {maks_feil(np.sin, lambda x: S(x, 3), x):.2e}", halv[-1])Utskrift:
like: True True
n = 1: maks feil 3.51e+01
n = 2: maks feil 4.65e+01
n = 3: maks feil 3.02e+01
n = 4: maks feil 1.19e+01
n = 5: maks feil 3.20e+00
n = 6: maks feil 6.25e-01
minste n: 10
[0, pi]: 7.52e-02, [0, 2pi]: 3.02e+01 3.1415926535897936
Svar på spørsmålene:
- a)
Svirker for arrays fordi**,*,/og+er vektorisert i NumPy.factorialfår bare vanlige heltall, så den skaper ikke problemer. - d) Tilnærmingene blir dårlige langt fra , fordi Taylorrekka er utviklet rundt 0. Høyere holder seg nær lenger ut.
- g) Maksfeilen på er mye mindre enn på . Feilen vokser raskt med , som plottet viser.