Implementere integrasjonsalgoritmar i Python.
I dette kapitlet skal vi bruke programmering til å rekne ut bestemte integral numerisk. Sjølv om mange integral kan løysast analytisk, finst det svært mange funksjonar som ikkje har ein lukka antiderivert. Då må vi ty til numeriske metodar.
Vi skal implementere tre klassiske metodar:
- Rektangelmetoden (den enklaste)
- Trapesmetoden (betre nøyaktigheit)
- Simpsons metode (svært god nøyaktigheit)
I tillegg skal vi sjå på desse metodane, som er fordjuping (utanfor læreplanen):
- Monte Carlo-integrasjon (stokastisk metode)
- Adaptiv integrasjon (automatisk steglengdejustering)
- Romberg-integrasjon (ekstrapolasjon)
- Gauss-kvadratur (optimal punktplassering)
- Dobbeltintegral (integrasjon i 2D)
Vi bruker Python-biblioteka NumPy og SciPy for effektiv implementering, og Matplotlib for visualisering.
Rektangelmetoden
Den enklaste tilnærminga til numerisk integrasjon er å dele opp arealet under kurva i rektangel. Idéen er at summen av areala til rektangla tilnærmar det bestemte integralet.
Venstrepunktsformelen:
der for .
Midtpunktsformelen (ofte meir nøyaktig):
Skriv ein Python-funksjon som reknar ut ved hjelp av rektangelmetoden med delintervall. Den eksakte verdien er .
def rektangelmetoden(f, a, b, n):
"""
Bereknar det bestemte
integralet av f frå a til b
ved hjelp av rektangelmetoden
(venstrepunkt).
Parametrar:
f: funksjonen som skal
integrerast
a: nedre grense
b: øvre grense
n: antal delintervall
Returnerer:
Tilnærma 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
# Berekn integralet
resultat = rektangelmetoden(
f, 0, 1, 1000
)
print(
"Tilnærma verdi:"
f" {resultat:.6f}"
)
print(f"Eksakt verdi: {1/3:.6f}")
print(
"Feil:"
f" {abs(resultat - 1/3):.6f}"
)Utskrift:
Tilnærma verdi: 0.332833
Eksakt verdi: 0.333333
Feil: 0.000500Feilen er ca. , noko som svarer til ein relativ feil på ca. .
Svar: Rektangelmetoden gir , medan den eksakte verdien er . Feilen er ca. .
Midtpunktsmetoden har faktisk dobbelt så god konvergensorden som venstrepunktsmetoden: i staden for .
Samanlikn 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"
f"{abs(v - eksakt):.2e}\t"
f"{abs(m - eksakt):.2e}"
)Utskrift:
n Venstrepunkt Midtpunkt
--------------------------------------------------
10 4.83e-02 8.33e-04
100 4.98e-03 8.33e-06
1000 5.00e-04 8.33e-08Midtpunktsmetoden er dramatisk meir nøyaktig!
Svar: Med er feilen ca. med venstrepunktsmetoden og ca. med midtpunktsmetoden.
Implementer rektangelmetoden for å rekne ut .
Den eksakte verdien er .
a) Bruk delintervall og finn tilnærma verdi.
b) Bruk delintervall og samanlikn feilen.
c) Kor mange delintervall treng du for at feilen skal vere mindre enn ?
Trapesmetoden
Trapesmetoden gir betre nøyaktigheit enn rektangelmetoden ved å tilnærme kurva med trapes i staden for rektangel. Eit trapes fangar opp meir av forma til kurva.
der og .
Alternativt kan dette skrivast som:
for ein eller annan .
Dette betyr at feilen avtek som når aukar, altså dobbelt så raskt som rektangelmetoden ().
Skriv ein Python-funksjon for trapesmetoden og rekn ut . Den eksakte verdien er .
import math
def trapesmetoden(f, a, b, n):
"""
Bereknar det bestemte
integralet av f frå 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 punkt (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}:"
f" {resultat:.8f}, feil ="
f" {feil:.2e}"
)Utskrift:
n = 10: 1.71971349, feil = 1.43e-03
n = 100: 1.71829615, feil = 1.43e-05
n = 1000: 1.71828197, feil = 1.43e-07Merk at feilen reduserast med faktor 100 når aukar med faktor 10, som stadfestar -oppførselen.
Svar: Trapesmetoden gir med (feil ) og med (feil ).
Samanlikn rektangelmetoden og trapesmetoden for integralet .
Den eksakte verdien er .
a) Implementer begge metodane.
b) Rekn ut integralet med for begge metodane.
c) Kor mange gonger meir nøyaktig er trapesmetoden?
Simpsons metode
Simpsons metode tilnærmar kurva med parablar (andregradsfunksjonar) i staden for rette linjer. Dette gir endå betre nøyaktigheit enn trapesmetoden, særleg for glatte funksjonar.
der .
Mønsteret for koeffisientane er: .
for ein eller annan .
Dette betyr at feilen avtek som , altså mykje raskare enn trapesmetoden.
Bemerkelsesverdig: Simpsons metode er eksakt for polynom av grad 3 eller lågare!
Implementer Simpsons metode og rekn ut med . Samanlikn med trapesmetoden.
import math
def simpson(f, a, b, n):
"""
Bereknar det bestemte
integralet av f frå a til b
ved hjelp av Simpsons metode.
Krev at n er eit partal.
"""
if n % 2 != 0:
raise ValueError(
"n må vere eit partal"
" for Simpsons metode"
)
h = (b - a) / n
# Første og siste ledd
total = f(a) + f(b)
# Odde indeksar (koeffisient
# 4)
for i in range(1, n, 2):
total += 4 * f(a + i * h)
# Like indeksar (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
# Samanlikn 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(
"Trapes (n=10): "
f" {trapes_10:.10f}, feil ="
f" {abs(trapes_10 - eksakt):.2e}"
)
print(
"Simpson (n=10):"
f" {simpson_10:.10f}, feil ="
f" {abs(simpson_10 - eksakt):.2e}"
)Utskrift:
Eksakt verdi: 2
Trapes (n=10): 1.9835235375, feil = 1.65e-02
Simpson (n=10): 2.0001095173, feil = 1.10e-04Med berre har Simpsons metode ein feil på ca. , medan trapesmetoden har feil på ca. . Simpsons metode er ca. 150 gonger meir nøyaktig.
Svar: Med gir Simpsons metode (feil ca. ) og trapesmetoden (feil ca. ).
Bruk Simpsons metode til å rekne ut .
Dette integralet har ingen lukka antiderivert, så vi må bruke numeriske metodar!
a) Implementer Simpsons metode.
b) Finn ein tilnærma verdi med .
c) Den "eksakte" verdien (rekna ut med mange desimalar) er ca. . Kva er feilen?
Simpsons 3/8-regel (fordjuping, utanfor læreplanen)
Det finst òg ein variant av Simpsons metode som bruker kubisk interpolasjon over tre delintervall.
For delintervall (der er deleleg med 3):
Mønsteret for koeffisientane er: .
Bruke NumPy og SciPy for integrasjon
I praksis treng vi ikkje alltid å implementere integrasjonsmetodar frå botnen av. Python-biblioteka NumPy og SciPy har ferdige, optimaliserte funksjonar for numerisk integrasjon.
- quad(f, a, b): Adaptiv integrasjon med høg nøyaktigheit. Returnerer (resultat, feilestimering).
- trapezoid(y, x): Trapesmetoden på diskrete datapunkt.
- simpson(y, x): Simpsons metode på diskrete datapunkt.
- 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 punkt.
NumPy gir òg numpy.trapezoid(y, x) for enkel trapesintegrasjon (i eldre NumPy-versjonar heiter ho numpy.trapz).
Bruk SciPy til å rekne ut .
import numpy as np
from scipy import integrate
# Definer funksjonen
def f(x):
return np.exp(-(x**2))
# Berekn integralet frå 0 til
# uendeleg
resultat, feil = integrate.quad(
f, 0, np.inf
)
eksakt = np.sqrt(np.pi) / 2
print(
"SciPy quad: "
f" {resultat:.10f}"
)
print(
f"Eksakt verdi: {eksakt:.10f}"
)
print(
"Feilestimering frå quad:"
f" {feil:.2e}"
)
print(
"Faktisk feil:"
f" {abs(resultat - eksakt):.2e}"
)Utskrift:
SciPy quad: 0.8862269255
Eksakt verdi: 0.8862269255
Feilestimering frå quad: 7.10e-09
Faktisk feil: 0.00e+00Merk at quad handterer uendelege grenser automatisk og gir ei feilestimering!
Svar: quad gir , som er lik med ti desimalar.
Gitt målepunkt frå eit eksperiment, rekn ut det tilnærma arealet under kurva.
import numpy as np
# Simulerte måledata (f.eks.
# hastigheit over tid)
# tid i sekund
t = np.array([0, 1, 2, 3, 4, 5])
# hastigheit i m/s
v = np.array([0, 5, 8, 10, 9, 6])
# Berekn tilbakelagd strekning
# (integral av hastigheit)
strekning = np.trapezoid(v, t)
print(f"Tidspunkt: {t}")
print(f"Hastigheiter: {v}")
print(
"Tilbakelagd strekning:"
f" {strekning:.1f} meter"
)Utskrift:
Tidspunkt: [0 1 2 3 4 5]
Hastigheiter: [ 0 5 8 10 9 6]
Tilbakelagd strekning: 35.0 meterDette er svært nyttig når vi har diskrete måledata i staden for ein analytisk funksjon.
Svar: Tilbakelagd strekning er ca. m.
Bruk SciPy til å rekne ut følgjande integral:
a)
b)
c)
Samanlikn med dei eksakte verdiane.
Visualisering av numerisk integrasjon
Ein viktig del av å forstå numeriske metodar er å visualisere kva som skjer. Med Matplotlib kan vi teikne funksjonen og rektangla/trapesa som blir brukte til å tilnærme arealet.
Lag ei visualisering som viser rektangla som blir brukte i rektangelmetoden for på med delintervall.
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(
'Rektangelmetoden: n ='
f' {n}\nTilnærmet:'
f' {integral_approx:.4f},'
' Eksakt:'
f' {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 ein figur som viser:
- Den blå kurva
- Lyseblå rektangel som representerer tilnærminga
- Tittel med både tilnærma og eksakt verdi
Lag ei visualisering som viser trapesmetoden for på med delintervall.
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(
'Trapesmetoden: n ='
f' {n}\nTilnærmet:'
f' {integral_approx:.4f},'
' Eksakt: 2.0000'
)
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Lag ei visualisering som samanliknar rektangelmetoden, trapesmetoden og Simpsons metode for på med .
Vis tre subplot side om side som illustrerer kvar metode, og rekn ut feilen for kvar.
Monte Carlo-integrasjon (fordjuping, utanfor læreplanen)
Ei heilt anna tilnærming til numerisk integrasjon er Monte Carlo-metoden, som bruker tilfeldige tal. Idéen er enkel: kast tilfeldige punkt i eit rektangel og tel kor mange som landar under kurva.
1. Generer tilfeldige -verdiar uniformt fordelte på .
2. Rekn ut gjennomsnittleg funksjonsverdi:
3. Multipliser med lengda til intervallet:
Fordelar:
- Enkel å implementere
- Skalerer godt til høgare dimensjonar
- Robust mot diskontinuitetar
Ulempe: Konvergerer langsamt, med feil .
Bruk Monte Carlo-metoden til å estimere ved å rekne ut .
import numpy as np
def monte_carlo_integral(
f, a, b, N
):
"""
Bereknar integralet av f frå a
til b
ved hjelp av Monte Carlo-
metoden.
"""
# Generer N tilfeldige
# x-verdiar i [a, b]
x_random = np.random.uniform(
a, b, N
)
# Berekn funksjonsverdiane
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)
# Køyr fleire gonger for å sjå
# variansen
# For reproduserbarheit
np.random.seed(42)
for N in [
100,
1000,
10000,
100000,
1000000,
]:
resultat = (
monte_carlo_integral(
f, -1, 1, N
)
)
# Heile sirkelen
pi_estimat = 2 * resultat
feil = abs(pi_estimat - np.pi)
print(
f"N = {N:7d}: pi ≈"
f" {pi_estimat:.6f}, feil"
f" = {feil:.6f}"
)Utskrift:
N = 100: pi ≈ 3.094681, feil = 0.046912
N = 1000: pi ≈ 3.114703, feil = 0.026890
N = 10000: pi ≈ 3.153273, feil = 0.011680
N = 100000: pi ≈ 3.144075, feil = 0.002482
N = 1000000: pi ≈ 3.141744, feil = 0.000151Feilen avtek i gjennomsnitt som : for å halvere feilen må vi firedoble talet på punkt. Éi einskild køyring følgjer ikkje dette nøyaktig, fordi punkta er tilfeldige.
Svar: Med punkt gir programmet estimatet for , med feil ca. .
Bruk Monte Carlo-integrasjon til å rekne ut volumet av ei einingssfære i 3D.
Volumet av ei sfære med radius er , så for er .
Hint: Generer tilfeldige punkt i kuben og tel kor mange som er innanfor sfæren (dvs. ).
Feilanalyse og konvergens
Når vi bruker numeriske metodar, er det viktig å forstå kor nøyaktig resultatet er og korleis feilen oppfører seg når vi aukar talet på delintervall.
| Metode | Feilorden |
|---|---|
| Rektangel (venstre) | |
| Rektangel (midtpunkt) | |
| Trapes | |
| Simpson | |
| Romberg | |
| Monte Carlo | |
| Metode | Dobling av |
| Rektangel (venstre) | Halverer feilen |
| Rektangel (midtpunkt) | Reduserer feilen med faktor 4 |
| Trapes | Reduserer feilen med faktor 4 |
| Simpson | Reduserer feilen med faktor 16 |
| Romberg | Avheng 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
Rekn ut konvergensordenen til trapesmetoden for ved å samanlikne 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"
f"{faktor:.2f}"
)
else:
print(
f"{n}\t\t{feil:.2e}\t"
"-"
)
prev_feil = feilUtskrift:
n Feil Faktor
----------------------------------------
10 1.43e-03 -
20 3.58e-04 4.00
40 8.95e-05 4.00
80 2.24e-05 4.00
160 5.59e-06 4.00
320 1.40e-06 4.00Faktoren er konsistent 4, som stadfestar at trapesmetoden har konvergensorden .
Svar: Feilen blir 4 gonger mindre kvar gong doblast, så trapesmetoden har konvergensorden 2.
Lag eit program som samanliknar konvergensen til rektangelmetoden, trapesmetoden og Simpsons metode for .
a) Rekn ut feilen for for alle tre metodane.
b) Lag eit log-log-plott av feil mot for alle tre metodane.
c) Stadfest konvergensordenane frå plottet (stigingstalet skal vere , og for dei respektive metodane).
Adaptiv integrasjon (fordjuping, utanfor læreplanen)
Så langt har vi brukt jamt fordelte delintervall. Men nokre funksjonar varierer meir i nokre område enn andre. Adaptiv integrasjon justerer steglengda automatisk: små steg der funksjonen varierer mykje, store steg der han er jamn.
1. Rekn ut integralet over heile intervallet:
2. Del intervallet i to og rekn ut summen: der
3. Samanlikn: Dersom (toleransen), godta som svar
4. Elles: Bruk adaptiv integrasjon rekursivt på og
Dette gir høg nøyaktigheit der det trengst, utan unødvendig utrekning der funksjonen er glatt.
Implementer adaptiv Simpson-integrasjon for funksjonar som varierer mykje lokalt, som på .
import numpy as np
def simpson_enkel(f, a, b):
"""Simpson over eitt
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.
Parametrar:
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
):
# Richardson-
# ekstrapolering
return (
S_sum
+ (S_sum - S_ab)
/ 15
)
# Rekursivt kall på kvar
# 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
)
# Berekn integralet
resultat = adaptiv_simpson(
f, 0, 1
)
# Samanlikn med SciPy
from scipy import integrate
scipy_resultat, _ = (
integrate.quad(f, 0, 1)
)
print(
"Adaptiv Simpson:"
f" {resultat:.10f}"
)
print(
"SciPy quad: "
f" {scipy_resultat:.10f}"
)
print(
"Forskjell: "
f" {abs(resultat - scipy_resultat):.2e}"
)Utskrift:
Adaptiv Simpson: 26.7794504459
SciPy quad: 26.7794504459
Forskjell: 1.82e-12Den adaptive metoden konsentrerer berekningane rundt toppen ved .
Svar: Integralet er . Adaptiv Simpson og quad gir same verdi (forskjell ca. ).
på intervallet .
a) Implementer ein teljar for funksjonsevalueringar.
b) Finn kor mange evalueringar vanleg Simpson treng for å oppnå feil .
c) Tel evalueringar for adaptiv Simpson med toleranse .
Romberg-integrasjon (fordjuping, utanfor læreplanen)
Romberg-integrasjon kombinerer trapesmetoden med Richardson-ekstrapolering for å oppnå høgare nøyaktigheit. Idéen er å bruke resultata frå trapesmetoden med forskjellige til å "ekstrapolere bort" feila.
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]
...Kvart ekstrapolasjonssteg aukar konvergensordenen med 2:
Implementer Romberg-integrasjon og rekn ut .
import numpy as np
def romberg(
f,
a,
b,
max_iter=10,
tol=1e-12,
):
"""
Romberg-integrasjon.
Returnerer:
(resultat, tabell) -
tilnærma 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 midtpunkt
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(
"Feil: "
f" {abs(resultat - eksakt):.2e}"
)Utskrift:
Romberg-tabell:
----------------------------------------------------------------------
i=0: 1.8591409142
i=1: 1.7539310925 1.7188611519
i=2: 1.7272219046 1.7183188419 1.7182826879
i=3: 1.7205185922 1.7182841547 1.7182818422 1.7182818288
i=4: 1.7188411286 1.7182819741 1.7182818287 1.7182818285 1.7182818285
i=5: 1.7184216603 1.7182818376 1.7182818285 1.7182818285 1.7182818285 1.7182818285
Resultat: 1.718281828459
Eksakt: 1.718281828459
Feil: 4.44e-16Merk den raske konvergensen: etter berre 5-6 iterasjonar har vi maskin-presisjon!
Svar: Romberg gir med feil ca. .
Bruk romberg-funksjonen frå dømet «Implementere Romberg-integrasjon» til å rekne ut .
a) Samanlikn med scipy.integrate.quad.
b) Skriv ut Romberg-tabellen og undersøk kor raskt diagonalen nærmar seg 2.
Gauss-kvadratur (fordjuping, utanfor læreplanen)
Gauss-kvadratur er ein familie av integrasjonsmetodar som oppnår maksimal nøyaktigheit for eit gitt tal på funksjonsevalueringar. I staden for å bruke jamt fordelte punkt, bruker Gauss-metodane optimalt plasserte punkt og vekter.
der punkta (nullpunkta til Legendre-polynoma) og vektene er optimalt valde.
Nøkkeleigenskap: Med punkt er Gauss-Legendre eksakt for alle polynom av grad .
For generelle grenser bruker vi variabelskiftet:
Bruk Gauss-Legendre-kvadratur til å rekne ut med forskjellig tal på punkt.
import numpy as np
from scipy import integrate
def f(x):
return np.exp(-(x**2))
# Eksakt verdi (frå 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}:"
f" {resultat:.12f}, feil"
f" = {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]
halvbredde = (b - a) / 2
sentrum = (a + b) / 2
x_trans = halvbredde * x + sentrum
scale = halvbredde
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}:"
f" {resultat:.12f}, feil"
" ="
f" {abs(resultat - eksakt):.2e}"
)Utskrift:
Gauss-Legendre-kvadratur:
--------------------------------------------------
n = 2: 0.746594688283, feil = 2.29e-04
n = 3: 0.746814584191, feil = 9.55e-06
n = 4: 0.746824468131, feil = 3.35e-07
n = 5: 0.746824126766, feil = 6.05e-09
n = 6: 0.746824132890, feil = 7.77e-11
n = 8: 0.746824132812, feil = 6.44e-15
n = 10: 0.746824132812, feil = 1.11e-16
Manuell Gauss-Legendre med numpy.polynomial.legendre:
n = 3: 0.746814584191, feil = 9.55e-06
n = 5: 0.746824126766, feil = 6.05e-09
n = 7: 0.746824132812, feil = 7.89e-13Med berre 10 punkter oppnår vi maskin-presisjon!
Svar: Gauss-Legendre med 10 punkter gir , med feil ca. .
Samanlikn Gauss-Legendre-kvadratur med Simpsons metode for følgjande integral:
a) (polynom av grad 5)
b) (oscillerande)
c) (singulær derivert ved )
For kvar: finn talet på punkt/delintervall som gir feil .
Dobbeltintegral numerisk (fordjuping, utanfor læreplanen)
Numerisk integrasjon i to dimensjonar er ei naturleg utviding av metodane vi har sett. Vi kan rekne ut
ved å gjenta éin-dimensjonal integrasjon.
For meir generelle område bruker vi scipy.integrate.dblquad:
der grensene for kan avhenge av .
Rekn ut der .
import numpy as np
from scipy import integrate
# NB: dblquad bruker rekkjefølgja
# (y, x)
def f(y, x):
return np.exp(-(x**2 + y**2))
# Berekn dobbeltintegralet
resultat, feil = (
integrate.dblquad(
f, 0, 1, 0, 1
)
)
print(
"Dobbeltintegral:"
f" {resultat:.10f}"
)
print(
f"Feilestimering: {feil:.2e}"
)
# Samanlikn 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(
"Iterert: "
f" {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(
"Simpson 2D: "
f" {resultat_simpson:.10f}"
)Utskrift:
Dobbeltintegral: 0.5577462854
Feilestimering: 8.29e-15
Iterert: 0.5577462854
Simpson 2D: 0.5577463616Svar: Dobbeltintegralet er .
Rekn ut arealet av einingssirkelen ved å rekne ut der er sirkelen .
import numpy as np
from scipy import integrate
# f(y, x) = 1 (vi bereknar 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)
# Berekn dobbeltintegralet
areal, feil = integrate.dblquad(
f, -1, 1, y_lower, y_upper
)
print(
f"Berekna areal: {areal:.10f}"
)
print(
"Eksakt (pi): "
f" {np.pi:.10f}"
)
print(
"Feil: "
f" {abs(areal - np.pi):.2e}"
)
# Alternativ: Monte Carlo
# For reproduserbarheit
np.random.seed(42)
N = 1000000
x = np.random.uniform(-1, 1, N)
y = np.random.uniform(-1, 1, N)
inside = np.sum(x**2 + y**2 <= 1)
# 4 = areal av kvadratet [-1,1] x
# [-1,1]
areal_mc = 4 * (inside / N)
print(
"\nMonte Carlo: "
f" {areal_mc:.4f}"
)
print(
"Feil MC: "
f" {abs(areal_mc - np.pi):.4f}"
)Utskrift:
Berekna areal: 3.1415926536
Eksakt (pi): 3.1415926536
Feil: 5.33e-15
Monte Carlo: 3.1399
Feil MC: 0.0017Svar: dblquad gir arealet . Monte Carlo med punkt gir (feil ca. ).
Rekn ut volumet under paraboloiden over sirkelen .
Det eksakte svaret er .
a) Bruk scipy.integrate.dblquad.
b) Bruk Monte Carlo med punkt.
c) Visualiser paraboloiden og integrasjonsområdet.
Praktiske bruksområde
Numerisk integrasjon har svært mange bruksområde i vitskap og ingeniørfag. Her ser vi på nokre eksempel.
Ei fjør følgjer Hookes lov med for små deformasjonar, men for store deformasjonar blir ho ikkje-lineær: . Rekn ut arbeidet som krevst for å strekkje fjøra frå til .
import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
# Parametrar
k = 100 # N/m (fjørstivheit)
# N/m^3 (ikkje-lineær koeffisient)
alpha = 500
L = 0.1 # m (strekning)
def F(x):
"""Kraft som funksjon av
posisjon"""
return k * x + alpha * x**3
# Berekn arbeidet W = integral av
# F(x) dx frå 0 til L
arbeid, feil = integrate.quad(
F, 0, L
)
# Samanlikn med lineær fjør (berre
# kx)
arbeid_linear = 0.5 * k * L**2
print(f"Ikkje-lineær fjør:")
print(
f" Arbeid = {arbeid:.4f} J"
)
print(f"\nLineær fjør:")
print(
" Arbeid ="
f" {arbeid_linear:.4f} J"
)
forskjell = (
(arbeid - arbeid_linear)
/ arbeid_linear
* 100
)
print(f"\nForskjell: {forskjell:.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=(
r'Ikkje-lineær: $F = kx +'
r' \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()Svar: Arbeidet er J for den ikkje-lineære fjøra og J for den lineære, ein forskjell på .
Rekn ut buelengda til kurva frå 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
)
# Berekn buelengda
buelengde, feil = integrate.quad(
integrand, 0, 2 * np.pi
)
print(
f"Buelengde av sin(x) frå 0"
f" til 2*pi:"
)
print(f"L = {buelengde:.6f}")
print(
"(Dette er ca."
f" {buelengde/np.pi:.4f}"
" * pi)"
)
# For referanse: lengda av ei rett
# linje frå (0,0) til (2*pi,0) er
# 2*pi
print(
f"\nRett linje: {2*np.pi:.6f}"
)
forlengelse = (
(buelengde - 2 * np.pi)
/ (2 * np.pi)
* 100
)
print(f"Forlengelse: {forlengelse:.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(
'$y = \sin(x)$, buelengde $L'
f' = {buelengde:.4f}$'
)
plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.show()Svar: Buelengda er .
a) Finn total tilbakelagd strekning i tidsintervallet s.
(Hint: strekning = , ikkje )
b) Finn netto forflytning (endeleg posisjon minus startposisjon).
c) Visualiser , posisjonen , og kumulativ strekning.
Oppsummering
Vi har lært om følgjande numeriske integrasjonsmetodar:
| Metode | Nøyaktigheit |
|---|---|
| Rektangel (venstre) | |
| Rektangel (midtpunkt) | |
| Trapes | |
| Simpson | |
| Romberg | |
| Gauss-Legendre | Optimal |
| Adaptiv | Variabel |
| Monte Carlo | |
| Metode | Implementering |
| Rektangel (venstre) | Enklast |
| Rektangel (midtpunkt) | Enkel |
| Trapes | Enkel |
| Simpson | Moderat |
| Romberg | Moderat |
| Gauss-Legendre | Krev vekter |
| Adaptiv | Kompleks |
| Monte Carlo | Enkel |
| Metode | Bruksområde |
| Rektangel (venstre) | Undervisning |
| Rektangel (midtpunkt) | Rask tilnærming |
| Trapes | God allround |
| Simpson | Glatte funksjonar |
| Romberg | Høy nøyaktigheit |
| Gauss-Legendre | Polynom, glatte funksjonar |
| Adaptiv | Varierande funksjonar |
| Monte Carlo | Høgdimensjonale integral |
Praktiske tips:
1. Bruk scipy.integrate.quad for dei fleste 1D-oppgåver
2. Bruk scipy.integrate.dblquad for 2D-integral
3. Bruk Monte Carlo for dimensjon
4. Bruk Romberg eller Gauss for høg presisjon
5. Verifiser alltid med kjende integral eller ved å auke
6. Visualiser for å byggje intuisjon!
Sannsynstettpunktet for normalfordelinga (Gauss-fordelinga) med gjennomsnitt og standardavvik er:
a) Verifiser at ved bruk av scipy.integrate.quad.
b) Berekn sannsynet , altså arealet under kurva frå til . Dette skal vere ca. (68,27 %).
c) Berekn og . Samanlikn med 68–95–99,7-regelen.
d) Visualiser normalfordelinga og skuggelegg området frå til .
Lag eit program som:
a) Tek inn ein funksjon , grenser , og tal på punkt .
b) Reknar ut integralet med alle metodane: rektangel, midtpunkt, trapes, Simpson, Romberg og Gauss-Legendre.
c) Viser ein tabell med resultata og feila (bruk scipy.quad som referanse).
d) Rangerer metodane etter nøyaktigheit for den gitte funksjonen.
Test med:
- på (polynom)
- på (oscillerande)
- på (Gaussisk)
Dersom integranden har singularitetar (uendelegheiter), vil standardmetodane feile. Bruk spesialiserte metodar eller del opp intervallet.
2. Oscillerande funksjonar:
Funksjonar som krev svært mange delintervall. Bruk adaptiv integrasjon.
3. Feil rekkefølgje i dblquad:scipy.integrate.dblquad bruker f(y, x), ikkje f(x, y)!
4. Gløyme partalskravet:
Simpsons metode krev at er eit partal.
5. Avrundingsfeil:
For svært små kan avrundingsfeil dominere. Bruk metodar av høgare orden i staden for å auke .
Oppsummering
I dette kapittelet har du lært:
- Rektangelmetoden: Tilnærmar integralet med ; venstrepunkt, høgrepunkt eller midtpunkt kan brukast.
- Trapesmetoden: , med feil av orden .
- Simpsons metode: Bruker parablar gjennom tre og tre punkt, med feil av orden — eksakt for polynom av grad .
- Python-implementasjon: Alle metodane kan skrivast som korte løkker eller med NumPy/SciPy (scipy.integrate).
- Monte Carlo-integrasjon: Bruker tilfeldige punkt til å estimere integral, nyttig i høge dimensjonar.
Nøkkelomgrep
| Omgrep | Forklaring |
|---|---|
| Rektangelmetoden | Sum av rektangelareal |
| Trapesmetoden | Sum av trapesareal, feil |
| Simpsons metode | Parabeltilnærming, feil |
| Monte Carlo | Estimat basert på tilfeldige punkt |
Viktige formlar
-
- 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.
