<

Differentialgleichungen

Runge-Kutta für eine DGl | .. mit Schrittweitensteuerung | Runge-Kutta-Fehlberg für eine DGl
Systeme von DGl und DGl höherer Ordnung

Die numerische Lösung von Differentialgleichungen (DG) kann schrittweise unter Nutzung der örtlichen Steigungswerte erfolgen, die durch die DG in der Form y'=f(x,y) gegeben sind.

Im einfachsten Falle berechnet man, beginnend bei einem Startwert xo, yo = y(xo) mit einem kleinen Schritt dx in x-Richtung einen Zielwert y(xo+dx) = yo + y'(xo) * dx. Geometrisch beschrieben: Es wird für die Lösung ein Abschnitt der Tangente genutzt, welche die Lösungskurve im Startpunkt berührt. Anschließend nimmt man diesen berechneten Punkt als Startwert einer zweiten Berechnung etc.
Dieses Verfahren trägt den Namen Leonhard Eulers (1707-1783) und tut ihm keine Ehre, denn es ergibt Lösungen, die mit zunehmender Schrittzahl systematisch von der wahren Lösung abweichen.
Natürlich würde ich dies hier jetzt nicht so ausdrücken, wenn es nicht bessere Lösungen gäbe:


Runge-Kutta-Verfahren für eine Differentialgleichung

Das Runge-Kutta-Verfahren vermeidet den Nachteil des Eulerverfahrens, indem es bei der Auswertung nicht linear, sondern mit einem Polynom höheren als ersten Grades extrapoliert. Dazu gibt es mehrere Varianten, ich stelle hier die verbreitetste, das "Runge-Kutta-Verfahren vierter Ordnung" vor. Der Grundalgorithmus mit den Hilfsvariablen a-d und der Intervallänge dx lautet wie folgt:

Function Funk (x As Single, y As Single) As Single
Funk = f (x, y)     'hier die Formel in Basicnotation einsetzen
End Function

Sub Runge_Kutta (xvon As Single, xbis As single, yvon As Single)

'Startwerte
x = xvon
y = yvon
'Berechnung für ein Intervall
Do
a = Funk ( x, y )
b = Funk ( x + dx / 2, y + dx * a / 2 )
c = Funk ( x + dx / 2, y + dx * b / 2 )
d = Funk ( x + dx, y + dx * c )
'neue Werte am Intervallende
y = y + dx * ( a + 2 * b + 2 * c + d ) / 6
x = x + dx
'Ausgabe
Printer.Print x, y
loop until x >= xbis    'Grenze des auszuwertenden Bereiches erreicht
End Sub

zum Anfang

Runge-Kutta-Verfahren mit Schrittweitensteuerung

Ein Nachteil der oben gezeigten einfachen Variante ist es, daß die Schrittweite dx für die ganze Auswertung den gleichen Wert besitzt. Man kann sich vorstellen, daß in Bereichen, in denen die Ableitung schwach veränderlich ist, große Schrittweiten, in denen mit hoher Veränderlichkeit dagegen kleine Schrittweiten angemessen wären.
Wenn wir schon einen Computer nutzen, soll er auch was tun und darüber von sich aus nachdenken! Das geschieht nicht in der angedeuteten Weise, vielmehr soll die Schrittweite vom Fehler derAnpassung gesteuert werden.
Die Idee ist, daß man zu einem Auswertungsintervall ddx prüft, ob der errechnete Wert yd über diese Länge von demjenigen abweicht, den man bei halbiertem Intervall und demgemäß nach zwei Schritten erhält, er heiße yd1. Solche Intervallteilung wird so lange fortgesetzt, bis die absolute Abweichung einen zuvor festgelegten kleinen Wert eps unterschreitet. Danach wird in der erforderlichen Schrittzahl der Funktionsverlauf innerhalb des gesamten dx-Intervalls berechnet. Erst der Endwert wird wieder ausgegeben.
Aus praktischen Gründen werden Routinen für die Ein-Schritt-( SubE_Runde) und die Mehrfach-Berechnung ( Sub M_Runde) ausgelagert. Dann sieht das Verfahren wie folgt aus:

Sub Runge_Kutta (ByVal xvon As Double, ByVal xbis As Double, ByVal dx As Double, yvon As Double)
Dim xd As Double, yd As Double, yd2 As Double
Dim x As Double, y As Double, ddx As Double, Ausgabe AsString
x = Xvon
y = Yvon
Ausgabe = "Runden" & "   " & "X-Werte" & "     " & "Y-Werte" & vbCrLf
Ausgabe = "   -  " & "   " & xvon & "      " & yvon & vbCrLf
Do
ddx = dx
Do
xd = x
yd = y
E_Runde xd, yd, ddx
Y_Zwi1 = yl
xd = x
yd1 = y
ddx = 0.5 * ddx
E_Runde xd, yd1, ddx
E_Runde xd, yd1, ddx
Loop Until Abs(yd - yd1) < eps
xd = x
yd = y
Hl = h
M_Runde x, x+dx, y, ddx
x = xd
y = yd
Ausgabe = Ausgabe & dx / ddx & "  " & x & "   " & y & vbCrLf
Loop Until x >= xbis
Printer.Print Ausgabe 'Drucker oder was man sonst will
End Sub

Sub E_Runde (x As Double, y As Double, ByVal dx As Double)
Dim a As Double, b As Double, c As Double, d As Double, ddx As Double
ddx = 0.5 * dx                              'Rechenaufwand senken!
a = Funk (x, y)
b = Funk (x + ddx, y + a * ddx)
c = Funk (x + ddx, y + b * ddx)
d = Funk (x + dx, y + c * dx)
y = y + dx * (a + 2 * b + 2 * c + d) / 6
x = x + dx
End Sub

Sub M_Runde (x As Double, ByVal xbis As Double, y As Double, ByVal dx As Double)
Dim a As Double, b As Double, c As Double, d As Double, ddx As Double
ddx = dx *0.5                               'Rechenaufwand senken!
While x < xbis - 0.5 * dx
a = Funk (x, y)
b = Funk (x + ddx, y + a * ddx)
c = Funk (x + ddx, y + b * ddx)
d = Funk (x + dx, y + c * h)
y = y + dx * (a + 2 * b + 2 * c + d) / 6
x = x + dx
Wend
End Sub
zum Anfang

Verfahren von Runge-Kutta-Fehlberg für eine Gleichung

Eine Weiterentwicklung des Runge-Kutta-Verfahrens, das Runge-Kutta-Fehlberg-Verfahren (RKF), verbessert die Schrittweitensteuerung in dem Sinne, daß im Verfahren ein in etwa konstantes Fehlerniveau gehalten wird. Wem der folgende Code wie Hokus-Pokus vorkommt, der lese nach bei: Paul L. De Vries, Computerphysik, Spektrum Akademischer Verlag 1995. Dort wird der folgende Algorithmus ausführlich begründet.

Sub RKF (ByVal xvon#, ByVal xbis#, ByVal yvon#)
'h und eps sind global vorgegeben!
Dim x#, y#, xs#, ys#
Dim i%, j%
Dim F#(5), A#(5), B#(5, 4), C#(5), D#(5)
Dim yhut#, Err#, MaxErr#, hNeu#
'Konstantensatz: Nicht verändern!
A(1) = 0.25: A(2) = 0.375: A(3) = 12 / 13: A(4) = 1:A(5) = 0.5
B(1, 0) = 0.25
B(2, 0) = 3 / 32: B(2, 1) = 9 /32   B(3, 0) = 1932 / 2197: B(3, 1) = -7200 / 2197:B(3,2) = 7296 / 2197
B(4, 0) = 439 / 216: B(4, 1) = -8: B(4, 2) = 3680 / 513: B(4, 3) = -845 / 4104
B(5, 0) = -8 / 27: B(5, 1) = 2: B(5, 2) = -3544 / 2565: B(5, 3) = 1859 / 4104: B(5, 4) = -11 / 40
C(0) = 16 / 135: C(1) = 0: C(2) = 6656 / 12825: C(3) = 28561 / 56430: C(4) = -9 / 50: C(5) = 2 / 55
D(0) = 1 / 360: D(1) = 0: D(2) = -128 / 4275: D(3) = -2197 / 75240: D(4) = 1 / 50: D(5) = 2 / 55
'jetzt geht es los!
xs = xvon: ys = yvon
Do
F(0) = Funk (xs, ys)
Do
For i = 1 To 5
x = xs + A(i) * h
y = 0
For j = 0 To i - 1
y = y + B(i, j) * F(j)
Next j
y = h * y + ys
F(i) = Funk (x, y)
Next i
yhut = 0: Err = 0
For i = 0 To 5
yhut = yhut + C(i) * F(i)
Err = Err + D(i) * F(i)
Next i
yhut = h * yhut + ys
Err = h * Abs(Err)
MaxErr = h * Eps
hNeu = 0.9 * h * Sqr(Sqr(MaxErr / Err))
If Err > MaxErr Then h = hNeu
Loop Until Err <= MaxErr
xs = xs + h
ys = yhut
'das sind die neuen Werte, hier abgreifen!
h = hNeu
Loop Until xs >= xbis
End Sub

Kleiner Schönheitsfehler dieser Verfahrensvariante: Die Stützpunkte, an denen Ausgaben erfolgen, hängen von der ermittelten, längs der Kurve variablen Schrittweite ab. Die zugehörigen x-Werte sind also aus Prinzip krumm. Dagegen könnte man natürlich etwas tun. Wenn man will (und kann).

zum Anfang

Systeme von Differentialgleichungen und Differentialgleichungen höherer Ordnung

Systeme von DGn kommen in der Praxis oft vor. Eines der bekanntesten Systeme ist das von Lotka und Volterra formulierte Differentialgleichungssystem für ein Räuber-Beute-System. Die bisher vorgestellten Verfahren lassen sich leicht auf Systeme von DGn erster Ordnung übertragen.
Ein neues Problem bedeuten DGn höherer Ordnung. Daß sie hier mit behandelt werde, hat einen guten Grund. Diese Gleichungen lassen sich nämlich auf Systeme linearer DGn reduzieren.
Wie das genau geht?

Gegeben sei z.B. y" = f(x, y, y'); man setzt y1 = yund y2= y' und erhält zwei unbestreitbar richtige Gleichungen: y1'= y2; y2' = f(x, y1, y2). That's it! Was brauchen wir mehr?

Der Algorithmus verkompliziert sich insofern, als aus linearen Größen, wie y , jetzt Vektoren y(i) werden. Nur x bleibt skalar. Im Aufruf der folgenden Routine bedeutet num die Anzahl simultan zu lösender Gleichungen. Der Algorithmus sieht etwa so aus:

Public Sub RKFN(ByVal xvon#, ByVal xbis#, ByVal yvon#(), ByVal num%)
'Global sind hmin, hmax, eps vorgegeben!
Dim x#, y#(10), xs#, ys#(10), H#, yalt#(10)
Dim i%, j%, k%
Dim F#(5, 10), A#(5), B#(5, 4), C#(5), D#(5)
Dim yhut#(10), Err#, MaxErr#, GrossErr#, HNeu#
'Konstantensatz: Nicht Verändern!
A(1) = 0.25: A(2) = 0.375: A(3) = 12 / 13: A(4) = 1: A(5) = 0.5
B(1, 0) = 0.25
B(2, 0) = 3 / 32: B(2, 1) = 9 / 32
B(3, 0) = 1932 / 2197: B(3, 1) = -7200 / 2197: B(3,2) = 7296 / 2197
B(4, 0) = 439 / 216: B(4, 1) = -8: B(4, 2) = 3680 / 513: B(4, 3) = -845 / 4104
B(5, 0) = -8 / 27: B(5, 1) = 2: B(5, 2) = -3544 / 2565: B(5, 3) = 1859 / 4104: B(5, 4) = -11 / 40
C(0) = 16 / 135: C(1) = 0: C(2) = 6656 / 12825: C(3) = 28561 / 56430: C(4) = -9 / 50: C(5) = 2 / 55
D(0) = 1 / 360: D(1) = 0: D(2) = -128 / 4275: D(3) = -2197 / 75240: D(4) = 1 / 50: D(5) = 2 / 55
H = hmax 
For k = 1 To num
yalt(k) = ys(k)
Next
xs = xvon
For k = 1 To num
ys(k) = yvon(k)
Next
Do
Funktion xs, ys(), F(), 0
Do
For i = 1 To 5
x = xs + A(i) * H
For k = 1 To num
y(k) = 0
For j = 0 To i - 1
y(k) = y(k) + B(i, j) * F(j, k)
Next j
y(k) = H * y(k) + ys(k)
Next k
Funktion x, y(), F(), i
Next i
GrossErr = 0
For k = 1 To num
yhut(k) = 0
Err = 0
For i = 0 To 5
yhut(k) = yhut(k) + C(i) * F(i, k)
Err = Err + D(i) * F(i, k)
Next
yhut(k) = H * yhut(k) + ys(k)
Err = H * Abs(Err)
If Err > GrossErr Then GrossErr = Err
Next k
MaxErr = H * Eps
HNeu = 0.9 * H*Sqr(Sqr(MaxErr / GrossErr))
If GrossErr > MaxErr Then H = HNeu
Loop Until GrossErr <= MaxErr
'Werte am Intervallende
xs = xs + H
For k = 1 To num
ys(k) = yhut(k)
Next
H = HNeu
Loop Until xs >= xbis
End Sub

Die Funktionsroutine, hier "Funktion" genannt, enthält die Differentialgleichungen und berechnet an der Stelle des x-Wertes und der diversen y-Werte den Satz der Funktionswerte y(i).

zum Anfang

© R. Hirte * 2003