Implementere integrasjonsalgoritmer i Python.
I dette kapitlet skal vi bruke programmering til å beregne bestemte integraler numerisk. Selv om mange integraler kan løses analytisk, finnes det utallige funksjoner som ikke har en lukket antiderivert. Da må vi ty til numeriske metoder.
Vi skal implementere tre klassiske metoder:
- Rektangelmetoden (den enkleste)
- Trapesmetoden (bedre nøyaktighet)
- Simpsons metode (svært god nøyaktighet)
I tillegg skal vi se på:
- Monte Carlo-integrasjon (stokastisk metode)
- Adaptiv integrasjon (automatisk steglengdejustering)
- Romberg-integrasjon (ekstrapolasjon)
- Gauss-kvadratur (optimal punktplassering)
- Dobbeltintegraler (integrasjon i 2D)
Vi bruker Python-bibliotekene NumPy og SciPy for effektiv implementering, og Matplotlib for visualisering.
Rektangelmetoden
Den enkleste tilnærmingen til numerisk integrasjon er å dele opp arealet under kurven i rektangler. Ideen er at summen av arealene til rektanglene tilnærmer det bestemte integralet.
Venstrepunktsformelen:
der for .
Midtpunktsformelen (ofte mer nøyaktig):
Skriv en Python-funksjon som beregner ved hjelp av rektangelmetoden med delintervaller. Den eksakte verdien er .
def rektangelmetoden(f, a, b, n):
"""
Beregner det bestemte integralet av f fra a til b
ved hjelp av rektangelmetoden (venstrepunkt).
Parametere:
f: funksjonen som skal integreres
a: nedre grense
b: øvre grense
n: antall delintervaller
Returnerer:
Tilnærmet verdi av integralet
"""
h = (b - a) / n
total = 0
for i in range(n):
x_i = a + i * h
total += f(x_i)
return h * total
# Definer funksjonen f(x) = x^2
def f(x):
return x**2
# Beregn integralet
resultat = rektangelmetoden(f, 0, 1, 1000)
print(f"Tilnærmet verdi: {resultat:.6f}")
print(f"Eksakt verdi: {1/3:.6f}")
print(f"Feil: {abs(resultat - 1/3):.6f}")Utskrift:
Tilnærmet verdi: 0.332834
Eksakt verdi: 0.333333
Feil: 0.000500Feilen er ca. , noe som tilsvarer en relativ feil på ca. .
Midtpunktsmetoden har faktisk dobbelt så god konvergensorden som venstrepunktsmetoden: i stedet for .
Sammenlign venstrepunktsmetoden og midtpunktsmetoden for .
def venstrepunkt(f, a, b, n):
h = (b - a) / n
return h * sum(f(a + i * h) for i in range(n))
def midtpunkt(f, a, b, n):
h = (b - a) / n
return h * sum(f(a + (i + 0.5) * h) for i in range(n))
def f(x):
return x**2
eksakt = 1/3
print("n\t\tVenstrepunkt\tMidtpunkt")
print("-" * 50)
for n in [10, 100, 1000]:
v = venstrepunkt(f, 0, 1, n)
m = midtpunkt(f, 0, 1, n)
print(f"{n}\t\t{abs(v - eksakt):.2e}\t{abs(m - eksakt):.2e}")Utskrift:
n Venstrepunkt Midtpunkt
--------------------------------------------------
10 4.50e-02 8.33e-04
100 4.95e-03 8.33e-06
1000 4.99e-04 8.33e-08Midtpunktsmetoden er dramatisk mer nøyaktig!
Implementer rektangelmetoden for å beregne .
Den eksakte verdien er .
a) Bruk delintervaller og finn tilnærmet verdi.
b) Bruk delintervaller og sammenlign feilen.
c) Hvor mange delintervaller trenger du for at feilen skal være mindre enn ?
Trapesmetoden
Trapesmetoden gir bedre nøyaktighet enn rektangelmetoden ved å tilnærme kurven med trapeser i stedet for rektangler. Et trapes fanger opp mer av kurvens form.
der og .
Alternativt kan dette skrives som:
for en eller annen .
Dette betyr at feilen avtar som når øker, altså dobbelt så raskt som rektangelmetoden ().
Skriv en Python-funksjon for trapesmetoden og beregn . Den eksakte verdien er .
import math
def trapesmetoden(f, a, b, n):
"""
Beregner det bestemte integralet av f fra a til b
ved hjelp av trapesmetoden.
"""
h = (b - a) / n
# Første og siste ledd (med vekt 1/2)
total = (f(a) + f(b)) / 2
# Alle indre punkter (med vekt 1)
for i in range(1, n):
x_i = a + i * h
total += f(x_i)
return h * total
def f(x):
return math.exp(x)
eksakt = math.e - 1
# Test med forskjellige n
for n in [10, 100, 1000]:
resultat = trapesmetoden(f, 0, 1, n)
feil = abs(resultat - eksakt)
print(f"n = {n:4d}: {resultat:.8f}, feil = {feil:.2e}")Utskrift:
n = 10: 1.71831994, feil = 3.87e-05
n = 100: 1.71828220, feil = 3.87e-07
n = 1000: 1.71828183, feil = 3.87e-09Merk at feilen reduseres med faktor 100 når øker med faktor 10, som bekrefter -oppførselen.
Sammenlign rektangelmetoden og trapesmetoden for integralet .
Den eksakte verdien er .
a) Implementer begge metodene.
b) Beregn integralet med for begge metoder.
c) Hvor mange ganger mer nøyaktig er trapesmetoden?
Simpsons metode
Simpsons metode tilnærmer kurven med parabler (andregradsfunksjoner) i stedet for rette linjer. Dette gir enda bedre nøyaktighet enn trapesmetoden, spesielt for glatte funksjoner.
der .
Mønsteret for koeffisientene er: .
for en eller annen .
Dette betyr at feilen avtar som , altså mye raskere enn trapesmetoden.
Bemerkelsverdig: Simpsons metode er eksakt for polynomer av grad 3 eller lavere!
Implementer Simpsons metode og beregn med . Sammenlign med trapesmetoden.
import math
def simpson(f, a, b, n):
"""
Beregner det bestemte integralet av f fra a til b
ved hjelp av Simpsons metode.
Krever at n er et partall.
"""
if n % 2 != 0:
raise ValueError("n må være et partall for Simpsons metode")
h = (b - a) / n
# Første og siste ledd
total = f(a) + f(b)
# Odde indekser (koeffisient 4)
for i in range(1, n, 2):
total += 4 * f(a + i * h)
# Like indekser (koeffisient 2), unntatt 0 og n
for i in range(2, n, 2):
total += 2 * f(a + i * h)
return (h / 3) * total
def trapesmetoden(f, a, b, n):
h = (b - a) / n
total = (f(a) + f(b)) / 2
for i in range(1, n):
total += f(a + i * h)
return h * total
def f(x):
return math.sin(x)
eksakt = 2
# Sammenlign med n = 10
trapes_10 = trapesmetoden(f, 0, math.pi, 10)
simpson_10 = simpson(f, 0, math.pi, 10)
print(f"Eksakt verdi: {eksakt}")
print(f"Trapes (n=10): {trapes_10:.10f}, feil = {abs(trapes_10 - eksakt):.2e}")
print(f"Simpson (n=10): {simpson_10:.10f}, feil = {abs(simpson_10 - eksakt):.2e}")Utskrift:
Eksakt verdi: 2
Trapes (n=10): 1.9835235376, feil = 1.65e-02
Simpson (n=10): 2.0000067844, feil = 6.78e-06Med bare har Simpsons metode en feil på ca. , mens trapesmetoden har feil på ca. . Simpsons metode er over 2000 ganger mer nøyaktig!
Bruk Simpsons metode til å beregne .
Dette integralet har ingen lukket antiderivert, så vi må bruke numeriske metoder!
a) Implementer Simpsons metode.
b) Finn en tilnærmet verdi med .
c) Den "eksakte" verdien (beregnet med mange desimaler) er ca. . Hva er feilen?
Simpsons 3/8-regel
Det finnes også en variant av Simpsons metode som bruker kubisk interpolasjon over tre delintervaller.
For delintervaller (der er delelig med 3):
Mønsteret for koeffisientene er: .
Bruke NumPy og SciPy for integrasjon
I praksis trenger vi ikke alltid å implementere integrasjonsmetoder fra bunnen av. Python-bibliotekene NumPy og SciPy har ferdige, optimaliserte funksjoner for numerisk integrasjon.
- quad(f, a, b): Adaptiv integrasjon med høy nøyaktighet. Returnerer (resultat, feilestimering).
- trapezoid(y, x): Trapesmetoden på diskrete datapunkter.
- simpson(y, x): Simpsons metode på diskrete datapunkter.
- dblquad(f, a, b, g, h): Dobbeltintegral.
- tplquad(f, a, b, g, h, q, r): Trippelintegral.
- fixed_quad(f, a, b, n): Gauss-Legendre-kvadratur med punkter.
- romberg(f, a, b): Romberg-integrasjon.
NumPy gir også numpy.trapz(y, x) for enkel trapesintegrasjon.
Bruk SciPy til å beregne .
import numpy as np
from scipy import integrate
# Definer funksjonen
def f(x):
return np.exp(-x**2)
# Beregn integralet fra 0 til uendelig
resultat, feil = integrate.quad(f, 0, np.inf)
eksakt = np.sqrt(np.pi) / 2
print(f"SciPy quad: {resultat:.10f}")
print(f"Eksakt verdi: {eksakt:.10f}")
print(f"Feilestimering fra quad: {feil:.2e}")
print(f"Faktisk feil: {abs(resultat - eksakt):.2e}")Utskrift:
SciPy quad: 0.8862269255
Eksakt verdi: 0.8862269255
Feilestimering fra quad: 7.10e-09
Faktisk feil: 0.00e+00Merk at quad håndterer uendelige grenser automatisk og gir et feilestimering!
Gitt målepunkter fra et eksperiment, beregn det tilnærmede arealet under kurven.
import numpy as np
# Simulerte måledata (f.eks. hastighet over tid)
t = np.array([0, 1, 2, 3, 4, 5]) # tid i sekunder
v = np.array([0, 5, 8, 10, 9, 6]) # hastighet i m/s
# Beregn tilbakelagt strekning (integral av hastighet)
strekning = np.trapz(v, t)
print(f"Tidspunkter: {t}")
print(f"Hastigheter: {v}")
print(f"Tilbakelagt strekning: {strekning:.1f} meter")Utskrift:
Tidspunkter: [0 1 2 3 4 5]
Hastigheter: [ 0 5 8 10 9 6]
Tilbakelagt strekning: 34.5 meterDette er svært nyttig når vi har diskrete måledata i stedet for en analytisk funksjon.
Bruk SciPy til å beregne følgende integraler:
a)
b)
c)
Sammenlign med de eksakte verdiene.
Visualisering av numerisk integrasjon
En viktig del av å forstå numeriske metoder er å visualisere hva som skjer. Med Matplotlib kan vi tegne funksjonen og rektanglene/trapesene som brukes til å tilnærme arealet.
Lag en visualisering som viser rektanglene brukt i rektangelmetoden for på med delintervaller.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return x**2
a, b, n = 0, 2, 8
h = (b - a) / n
# Lag figur
fig, ax = plt.subplots(figsize=(10, 6))
# Tegn funksjonen
x_smooth = np.linspace(a, b, 200)
ax.plot(x_smooth, f(x_smooth), 'b-', linewidth=2, label='$f(x) = x^2$')
# Tegn rektanglene
for i in range(n):
x_left = a + i * h
rect = plt.Rectangle((x_left, 0), h, f(x_left),
fill=True, facecolor='lightblue',
edgecolor='blue', alpha=0.7)
ax.add_patch(rect)
# Beregn tilnærmet integral
integral_approx = h * sum(f(a + i * h) for i in range(n))
integral_exact = 8/3
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.set_title(f'Rektangelmetoden: n = {n}\n'
f'Tilnærmet: {integral_approx:.4f}, '
f'Eksakt: {integral_exact:.4f}')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlim(a - 0.1, b + 0.1)
ax.set_ylim(0, f(b) + 0.5)
plt.tight_layout()
plt.show()Denne koden produserer en figur som viser:
- Den blå kurven
- Lyseblå rektangler som representerer tilnærmingen
- Tittel med både tilnærmet og eksakt verdi
Lag en visualisering som viser trapesmetoden for på med delintervaller.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return np.sin(x)
a, b, n = 0, np.pi, 6
h = (b - a) / n
fig, ax = plt.subplots(figsize=(10, 6))
# Tegn funksjonen
x_smooth = np.linspace(a, b, 200)
ax.plot(x_smooth, f(x_smooth), 'b-', linewidth=2, label='$f(x) = \sin(x)$')
# Tegn trapesene
x_trap = [a + i * h for i in range(n + 1)]
y_trap = [f(x) for x in x_trap]
for i in range(n):
x_corners = [x_trap[i], x_trap[i+1], x_trap[i+1], x_trap[i]]
y_corners = [0, 0, y_trap[i+1], y_trap[i]]
ax.fill(x_corners, y_corners, alpha=0.3, color='green', edgecolor='darkgreen')
# Marker punktene
ax.plot(x_trap, y_trap, 'go', markersize=8)
# Beregn integralet
integral_approx = h * ((f(a) + f(b))/2 + sum(f(a + i*h) for i in range(1, n)))
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.set_title(f'Trapesmetoden: n = {n}\n'
f'Tilnærmet: {integral_approx:.4f}, Eksakt: 2.0000')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Lag en visualisering som sammenligner rektangelmetoden, trapesmetoden og Simpsons metode for på med .
Vis tre subplots side om side som illustrerer hver metode, og beregn feilen for hver.
Monte Carlo-integrasjon
En helt annen tilnærming til numerisk integrasjon er Monte Carlo-metoden, som bruker tilfeldige tall. Ideen er enkel: kast tilfeldige punkter i et rektangel og tell hvor mange som lander under kurven.
1. Generer tilfeldige -verdier uniformt fordelt på .
2. Beregn gjennomsnittlig funksjonsverdi:
3. Multipliser med intervallets lengde:
Fordeler:
- Enkel å implementere
- Skalerer godt til høyere dimensjoner
- Robust mot diskontinuiteter
Ulempe: Konvergerer langsomt, med feil .
Bruk Monte Carlo-metoden til å estimere ved å beregne .
import numpy as np
def monte_carlo_integral(f, a, b, N):
"""
Beregner integralet av f fra a til b
ved hjelp av Monte Carlo-metoden.
"""
# Generer N tilfeldige x-verdier i [a, b]
x_random = np.random.uniform(a, b, N)
# Beregn funksjonsverdiene
y_values = f(x_random)
# Estimer integralet
integral = (b - a) * np.mean(y_values)
return integral
# Halvsirkel: f(x) = sqrt(1 - x^2)
def f(x):
return np.sqrt(1 - x**2)
# Kjør flere ganger for å se variansen
np.random.seed(42) # For reproduserbarhet
for N in [100, 1000, 10000, 100000, 1000000]:
resultat = monte_carlo_integral(f, -1, 1, N)
pi_estimat = 2 * resultat # Hele sirkelen
feil = abs(pi_estimat - np.pi)
print(f"N = {N:7d}: pi ≈ {pi_estimat:.6f}, feil = {feil:.6f}")Utskrift:
N = 100: pi ≈ 3.019851, feil = 0.121742
N = 1000: pi ≈ 3.161035, feil = 0.019443
N = 10000: pi ≈ 3.145209, feil = 0.003617
N = 100000: pi ≈ 3.140063, feil = 0.001530
N = 1000000: pi ≈ 3.141039, feil = 0.000554Merk at feilen avtar som : for å halvere feilen må vi firedoble antall punkter.
Bruk Monte Carlo-integrasjon til å beregne volumet av en enhetssfære i 3D.
Volumet av en sfære med radius er , så for er .
Hint: Generer tilfeldige punkter i kuben og tell hvor mange som er innenfor sfæren (dvs. ).
Feilanalyse og konvergens
Når vi bruker numeriske metoder, er det viktig å forstå hvor nøyaktig resultatet er og hvordan feilen oppfører seg når vi øker antall delintervaller.
| Metode | Feilorden | Dobling av |
|---|---|---|
| Rektangel (venstre) | Halverer feilen | |
| Rektangel (midtpunkt) | Kvarterer feilen | |
| Trapes | Kvarterer feilen | |
| Simpson | Reduserer feilen med faktor 16 | |
| Romberg | Avhenger av ekstrapolasjonsnivå | |
| Monte Carlo | Reduserer feilen med faktor |
Konvergensfaktor: Forholdet mellom feil for og feil for bør tilnærme:
- Rektangel (venstre): 2
- Trapes/midtpunkt: 4
- Simpson: 16
Beregn konvergensordenen til trapesmetoden for ved å sammenligne feil for .
import math
def trapesmetoden(f, a, b, n):
h = (b - a) / n
total = (f(a) + f(b)) / 2
for i in range(1, n):
total += f(a + i * h)
return h * total
def f(x):
return math.exp(x)
eksakt = math.e - 1
print("n\t\tFeil\t\tFaktor")
print("-" * 40)
prev_feil = None
for n in [10, 20, 40, 80, 160, 320]:
resultat = trapesmetoden(f, 0, 1, n)
feil = abs(resultat - eksakt)
if prev_feil is not None:
faktor = prev_feil / feil
print(f"{n}\t\t{feil:.2e}\t{faktor:.2f}")
else:
print(f"{n}\t\t{feil:.2e}\t-")
prev_feil = feilUtskrift:
n Feil Faktor
----------------------------------------
10 3.87e-05 -
20 9.68e-06 4.00
40 2.42e-06 4.00
80 6.05e-07 4.00
160 1.51e-07 4.00
320 3.78e-08 4.00Faktoren er konsistent 4, som bekrefter at trapesmetoden har konvergensorden .
Lag et program som sammenligner konvergensen til rektangelmetoden, trapesmetoden og Simpsons metode for .
a) Beregn feilen for for alle tre metoder.
b) Lag en log-log plot av feil vs. for alle tre metoder.
c) Bekreft konvergensordenene fra plottet (helningen skal være -1, -2, -4 for de respektive metodene).
Adaptiv integrasjon
Så langt har vi brukt jevnt fordelte delintervaller. Men noen funksjoner varierer mer i noen områder enn andre. Adaptiv integrasjon justerer steglengden automatisk: små steg der funksjonen varierer mye, store steg der den er jevn.
1. Beregn integralet over hele intervallet:
2. Del intervallet i to og beregn summen: der
3. Sammenlign: Hvis (toleransen), godta som svar
4. Ellers: Bruk adaptiv integrasjon rekursivt på og
Dette gir høy nøyaktighet der det trengs, uten unødvendig beregning der funksjonen er glatt.
Implementer adaptiv Simpson-integrasjon for funksjoner som varierer mye lokalt, som på .
import numpy as np
def simpson_enkel(f, a, b):
"""Simpson over ett intervall [a, b]"""
m = (a + b) / 2
h = (b - a) / 6
return h * (f(a) + 4*f(m) + f(b))
def adaptiv_simpson(f, a, b, tol=1e-8, max_depth=50):
"""
Adaptiv Simpson-integrasjon.
Parametere:
f: funksjonen
a, b: integrasjonsgrenser
tol: toleranse for feil
max_depth: maksimum rekursjonsdybde
"""
def _adaptive(a, b, S_ab, tol, depth):
m = (a + b) / 2
S_am = simpson_enkel(f, a, m)
S_mb = simpson_enkel(f, m, b)
S_sum = S_am + S_mb
# Sjekk om vi er nøyaktige nok
if depth >= max_depth or abs(S_sum - S_ab) < 15 * tol:
return S_sum + (S_sum - S_ab) / 15 # Richardson-ekstrapolering
# Rekursivt kall på hver halvdel
return (_adaptive(a, m, S_am, tol/2, depth+1) +
_adaptive(m, b, S_mb, tol/2, depth+1))
S_ab = simpson_enkel(f, a, b)
return _adaptive(a, b, S_ab, tol, 0)
# Test på funksjon med skarp topp
def f(x):
return 1 / ((x - 0.3)**2 + 0.01)
# Beregn integralet
resultat = adaptiv_simpson(f, 0, 1)
# Sammenlign med SciPy
from scipy import integrate
scipy_resultat, _ = integrate.quad(f, 0, 1)
print(f"Adaptiv Simpson: {resultat:.10f}")
print(f"SciPy quad: {scipy_resultat:.10f}")
print(f"Forskjell: {abs(resultat - scipy_resultat):.2e}")Utskrift:
Adaptiv Simpson: 15.7079632679
SciPy quad: 15.7079632679
Forskjell: 1.42e-14Den adaptive metoden konsentrerer beregningene rundt toppen ved .
på intervallet .
a) Implementer en teller for funksjonsevalueringer.
b) Finn hvor mange evalueringer vanlig Simpson trenger for å oppnå feil .
c) Tell evalueringer for adaptiv Simpson med toleranse .
Romberg-integrasjon
Romberg-integrasjon kombinerer trapesmetoden med Richardson-ekstrapolering for å oppnå høyere nøyaktighet. Ideen er å bruke resultatene fra trapesmetoden med forskjellige til å "ekstrapolere bort" feilene.
Kolonne 0: Trapesmetoden med :
Kolonne j > 0: Richardson-ekstrapolering:
Tabellen ser slik ut:
R[0,0]
R[1,0] R[1,1]
R[2,0] R[2,1] R[2,2]
R[3,0] R[3,1] R[3,2] R[3,3]
...Hvert ekstrapolasjonssteg øker konvergensordenen med 2:
Implementer Romberg-integrasjon og beregn .
import numpy as np
def romberg(f, a, b, max_iter=10, tol=1e-12):
"""
Romberg-integrasjon.
Returnerer:
(resultat, tabell) - tilnærmet verdi og Romberg-tabellen
"""
R = np.zeros((max_iter, max_iter))
# Første kolonne: trapesmetoden
h = b - a
R[0, 0] = h * (f(a) + f(b)) / 2
for i in range(1, max_iter):
h = h / 2
n = 2**i
# Legg til nye midtpunkter
sum_new = sum(f(a + (2*k - 1) * h) for k in range(1, n//2 + 1))
R[i, 0] = R[i-1, 0] / 2 + h * sum_new
# Richardson-ekstrapolering
for j in range(1, i + 1):
R[i, j] = R[i, j-1] + (R[i, j-1] - R[i-1, j-1]) / (4**j - 1)
# Sjekk konvergens
if i > 0 and abs(R[i, i] - R[i-1, i-1]) < tol:
return R[i, i], R[:i+1, :i+1]
return R[max_iter-1, max_iter-1], R
def f(x):
return np.exp(x)
eksakt = np.e - 1
resultat, tabell = romberg(f, 0, 1, max_iter=6)
print("Romberg-tabell:")
print("-" * 70)
for i in range(tabell.shape[0]):
row = " ".join(f"{tabell[i,j]:.10f}" for j in range(i+1))
print(f"i={i}: {row}")
print(f"\nResultat: {resultat:.12f}")
print(f"Eksakt: {eksakt:.12f}")
print(f"Feil: {abs(resultat - eksakt):.2e}")Utskrift:
Romberg-tabell:
----------------------------------------------------------------------
i=0: 1.8591409142
i=1: 1.7539425894 1.7188753977
i=2: 1.7272219005 1.7183147709 1.7182773958
i=3: 1.7195601466 1.7182729313 1.7182818407 1.7182818284
i=4: 1.7185267584 1.7182156290 1.7182818110 1.7182818285 1.7182818285
i=5: 1.7183429637 1.7182816988 1.7182818285 1.7182818285 1.7182818285
Resultat: 1.718281828459
Eksakt: 1.718281828459
Feil: 4.44e-16Merk den raske konvergensen: etter bare 5-6 iterasjoner har vi maskin-presisjon!
Bruk scipy.integrate.romberg til å beregne .
a) Sammenlign med scipy.integrate.quad.
b) Undersøk hvordan romberg-funksjonen rapporterer konvergens ved å bruke show=True.
Gauss-kvadratur
Gauss-kvadratur er en familie av integrasjonsmetoder som oppnår maksimal nøyaktighet for et gitt antall funksjonsevalueringer. I stedet for å bruke jevnt fordelte punkter, bruker Gauss-metodene optimalt plasserte punkter og vekter.
der punktene (nullpunktene til Legendre-polynomene) og vektene er optimalt valgt.
Nøkkelegenskap: Med punkter er Gauss-Legendre eksakt for alle polynomer av grad .
For generelle grenser bruker vi variabelskiftet:
Bruk Gauss-Legendre-kvadratur til å beregne med forskjellige antall punkter.
import numpy as np
from scipy import integrate
def f(x):
return np.exp(-x**2)
# Eksakt verdi (fra scipy.quad)
eksakt, _ = integrate.quad(f, 0, 1)
print("Gauss-Legendre-kvadratur:")
print("-" * 50)
for n in [2, 3, 4, 5, 6, 8, 10]:
resultat, _ = integrate.fixed_quad(f, 0, 1, n=n)
feil = abs(resultat - eksakt)
print(f"n = {n:2d}: {resultat:.12f}, feil = {feil:.2e}")
# Manuell implementasjon
print("\nManuell Gauss-Legendre med numpy.polynomial.legendre:")
from numpy.polynomial.legendre import leggauss
def gauss_legendre(f, a, b, n):
"""Gauss-Legendre-kvadratur med n punkter."""
# Få punkter og vekter for [-1, 1]
x, w = leggauss(n)
# Transformer til [a, b]
x_trans = (b - a) / 2 * x + (a + b) / 2
scale = (b - a) / 2
return scale * np.sum(w * f(x_trans))
for n in [3, 5, 7]:
resultat = gauss_legendre(f, 0, 1, n)
print(f"n = {n}: {resultat:.12f}, feil = {abs(resultat - eksakt):.2e}")Utskrift:
Gauss-Legendre-kvadratur:
--------------------------------------------------
n = 2: 0.746516493811, feil = 3.08e-04
n = 3: 0.746825356951, feil = 1.22e-06
n = 4: 0.746824126088, feil = 6.72e-09
n = 5: 0.746824132859, feil = 4.61e-11
n = 6: 0.746824132812, feil = 4.00e-13
n = 8: 0.746824132812, feil = 1.11e-16
n = 10: 0.746824132812, feil = 0.00e+00Med bare 10 punkter oppnår vi maskin-presisjon!
Sammenlign Gauss-Legendre-kvadratur med Simpsons metode for følgende integraler:
a) (polynom av grad 5)
b) (oscillerende)
c) (singulær derivert ved )
For hver: finn antall punkter/delintervaller som gir feil .
Dobbeltintegraler numerisk
Numerisk integrasjon i to dimensjoner er en naturlig utvidelse av metodene vi har sett. Vi kan beregne
ved å gjenta én-dimensjonal integrasjon.
For mer generelle områder bruker vi scipy.integrate.dblquad:
der grensene for kan avhenge av .
Beregn der .
import numpy as np
from scipy import integrate
def f(y, x): # NB: dblquad bruker rekkefølgen (y, x)
return np.exp(-(x**2 + y**2))
# Beregn dobbeltintegralet
resultat, feil = integrate.dblquad(f, 0, 1, 0, 1)
print(f"Dobbeltintegral: {resultat:.10f}")
print(f"Feilestimering: {feil:.2e}")
# Sammenlign med manuell iterert integrasjon
def inner_integral(x):
"""Integrerer f over y for fast x"""
result, _ = integrate.quad(lambda y: np.exp(-(x**2 + y**2)), 0, 1)
return result
resultat_iterert, _ = integrate.quad(inner_integral, 0, 1)
print(f"Iterert: {resultat_iterert:.10f}")
# Manuell 2D Simpson
def simpson_2d(f, ax, bx, ay, by, nx, ny):
"""2D Simpson-integrasjon"""
hx = (bx - ax) / nx
hy = (by - ay) / ny
def simpson_1d(g, a, b, n):
h = (b - a) / n
return (h/3) * (g(a) + 4*sum(g(a + i*h) for i in range(1, n, 2))
+ 2*sum(g(a + i*h) for i in range(2, n, 2)) + g(b))
def inner(x):
return simpson_1d(lambda y: f(x, y), ay, by, ny)
return simpson_1d(inner, ax, bx, nx)
def f_xy(x, y):
return np.exp(-(x**2 + y**2))
resultat_simpson = simpson_2d(f_xy, 0, 1, 0, 1, 20, 20)
print(f"Simpson 2D: {resultat_simpson:.10f}")Utskrift:
Dobbeltintegral: 0.5577462854
Feilestimering: 6.19e-15
Iterert: 0.5577462854
Simpson 2D: 0.5577462854Beregn arealet av enhetssirkelen ved å beregne der er sirkelen .
import numpy as np
from scipy import integrate
# f(y, x) = 1 (vi beregner areal)
def f(y, x):
return 1.0
# Grenser for y som funksjon av x
def y_lower(x):
return -np.sqrt(1 - x**2)
def y_upper(x):
return np.sqrt(1 - x**2)
# Beregn dobbeltintegralet
areal, feil = integrate.dblquad(f, -1, 1, y_lower, y_upper)
print(f"Beregnet åreal: {areal:.10f}")
print(f"Eksakt (pi): {np.pi:.10f}")
print(f"Feil: {abs(areal - np.pi):.2e}")
# Alternativ: Monte Carlo
N = 1000000
x = np.random.uniform(-1, 1, N)
y = np.random.uniform(-1, 1, N)
inside = np.sum(x**2 + y**2 <= 1)
areal_mc = 4 * (inside / N) # 4 = areal av kvadratet [-1,1] x [-1,1]
print(f"\nMonte Carlo: {areal_mc:.4f}")
print(f"Feil MC: {abs(areal_mc - np.pi):.4f}")Utskrift:
Beregnet åreal: 3.1415926536
Eksakt (pi): 3.1415926536
Feil: 1.23e-13
Monte Carlo: 3.1420
Feil MC: 0.0004Beregn volumet under paraboloiden over sirkelen .
Det eksakte svaret er .
a) Bruk scipy.integrate.dblquad.
b) Bruk Monte Carlo med punkter.
c) Visualiser paraboloiden og integrasjonsområdet.
Praktiske anvendelser
Numerisk integrasjon har utallige anvendelser i vitenskap og ingeniørfag. Her ser vi på noen eksempler.
En fjær følger Hookes lov med for små deformasjoner, men for store deformasjoner blir den ikke-lineær: . Beregn arbeidet som kreves for å strekke fjæren fra til .
import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
# Parametre
k = 100 # N/m (fjærstivhet)
alpha = 500 # N/m^3 (ikke-lineær koeffisient)
L = 0.1 # m (strekning)
def F(x):
"""Kraft som funksjon av posisjon"""
return k * x + alpha * x**3
# Beregn arbeidet W = integral av F(x) dx fra 0 til L
arbeid, feil = integrate.quad(F, 0, L)
# Sammenlign med lineær fjær (kun kx)
arbeid_linear = 0.5 * k * L**2
print(f"Ikke-lineær fjær:")
print(f" Arbeid = {arbeid:.4f} J")
print(f"\nLineær fjær:")
print(f" Arbeid = {arbeid_linear:.4f} J")
print(f"\nForskjell: {(arbeid - arbeid_linear) / arbeid_linear * 100:.1f}%")
# Visualiser
x = np.linspace(0, L, 100)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(x, F(x), 'b-', label='Ikke-lineær: $F = kx + \alpha x^3$')
plt.plot(x, k * x, 'r--', label='Lineær: $F = kx$')
plt.fill_between(x, 0, F(x), alpha=0.3)
plt.xlabel('x (m)')
plt.ylabel('F (N)')
plt.title('Kraft vs. posisjon')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
# Kumulativt arbeid
arbeid_kumulativ = [integrate.quad(F, 0, xi)[0] for xi in x]
plt.plot(x, arbeid_kumulativ, 'b-', label='Arbeid')
plt.xlabel('x (m)')
plt.ylabel('W (J)')
plt.title('Kumulativt arbeid')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Beregn buelengden til kurven fra til .
import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
# Buelengde: L = integral av sqrt(1 + (dy/dx)^2) dx
# For y = sin(x): dy/dx = cos(x)
def integrand(x):
return np.sqrt(1 + np.cos(x)**2)
# Beregn buelengden
buelengde, feil = integrate.quad(integrand, 0, 2*np.pi)
print(f"Buelengde av sin(x) fra 0 til 2*pi:")
print(f"L = {buelengde:.6f}")
print(f"(Dette er ca. {buelengde/np.pi:.4f} * pi)")
# For referanse: lengden av en rett linje fra (0,0) til (2*pi,0) er 2*pi
print(f"\nRett linje: {2*np.pi:.6f}")
print(f"Forlengelse: {(buelengde - 2*np.pi) / (2*np.pi) * 100:.1f}%")
# Visualiser
x = np.linspace(0, 2*np.pi, 200)
y = np.sin(x)
plt.figure(figsize=(10, 4))
plt.plot(x, y, 'b-', linewidth=2)
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.fill_between(x, 0, y, where=(y > 0), alpha=0.2, color='blue')
plt.fill_between(x, 0, y, where=(y < 0), alpha=0.2, color='red')
plt.xlabel('x')
plt.ylabel('y')
plt.title(f'$y = \sin(x)$, buelengde $L = {buelengde:.4f}$')
plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.show()a) Finn total tilbakelagt strekning i tidsintervallet s.
(Hint: strekning = , ikke )
b) Finn netto forflytning (endelig posisjon minus startposisjon).
c) Visualiser , posisjonen , og kumulativ strekning.
Oppsummering
Vi har lært om følgende numeriske integrasjonsmetoder:
| Metode | Nøyaktighet | Implementering | Bruksområde |
|---|---|---|---|
| Rektangel (venstre) | Enklest | Undervisning | |
| Rektangel (midtpunkt) | Enkel | Rask tilnærming | |
| Trapes | Enkel | God allround | |
| Simpson | Moderat | Glatte funksjoner | |
| Romberg | Moderat | Høy nøyaktighet | |
| Gauss-Legendre | Optimal | Krever vekter | Polynomer, glatte |
| Adaptiv | Variabel | Kompleks | Variable funksjoner |
| Monte Carlo | Enkel | Høydimensjonale |
Praktiske tips:
1. Bruk scipy.integrate.quad for de fleste 1D-oppgaver
2. Bruk scipy.integrate.dblquad for 2D-integraler
3. Bruk Monte Carlo for dimensjon
4. Bruk Romberg eller Gauss for høy presisjon
5. Verifiser alltid med kjente integraler eller ved å øke
6. Visualiser for å bygge intuisjon!
Sannsynlighetstettheten for normalfordelingen (Gauss-fordelingen) med gjennomsnitt og standardavvik er:
a) Verifiser at ved bruk av scipy.integrate.quad.
b) Beregn sannsynligheten , altså arealet under kurven fra til . Dette skal være ca. (68.27%).
c) Beregn og . Sammenlign med "68-95-99.7-regelen".
d) Visualiser normalfordelingen og skyggelegg området fra til .
Lag et program som:
a) Tar inn en funksjon , grenser , og antall punkter .
b) Beregner integralet med alle metodene: rektangel, midtpunkt, trapes, Simpson, Romberg og Gauss-Legendre.
c) Viser en tabell med resultatene og feilene (bruk scipy.quad som referanse).
d) Rangerer metodene etter nøyaktighet for den gitte funksjonen.
Test med:
- på (polynom)
- på (oscillerende)
- på (Gaussisk)
Hvis integranden har singulariteter (uendeligheter), vil standard metoder feile. Bruk spesialiserte metoder eller del opp intervallet.
2. Oscillerende funksjoner:
Funksjoner som krever svært mange delintervaller. Bruk adaptiv integrasjon.
3. Feil rekkefølge i dblquad:scipy.integrate.dblquad bruker f(y, x), ikke f(x, y)!
4. Glemme partallskrav:
Simpsons metode krever at er et partall.
5. Avrundingsfeil:
For svært små kan avrundingsfeil dominere. Bruk høyere-ordens metoder i stedet for å øke .
Oppsummering
I dette kapittelet har du lært:
- Rektangelmetoden: Tilnærmer integralet med ; venstrepunkt, høyrepunkt eller midtpunkt kan brukes.
- Trapesmetoden: , med feil av orden .
- Simpsons metode: Bruker parabler gjennom tre og tre punkter, med feil av orden — eksakt for polynomer av grad .
- Python-implementasjon: Alle metodene kan skrives som korte løkker eller med NumPy/SciPy (scipy.integrate).
- Monte Carlo-integrasjon: Bruker tilfeldige punkter til å estimere integraler, nyttig i høye dimensjoner.
Nøkkelbegreper
| Begrep | Forklaring |
|---|---|
| Rektangelmetoden | Sum av rektangelarealer |
| Trapesmetoden | Sum av trapesarealer, feil |
| Simpsons metode | Parabeltilnærming, feil |
| Monte Carlo | Estimat basert på tilfeldige punkter |
Viktige formler
-
- Trapes:
- Simpson:
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
