Berechnung bestimmter Integrale: grundlegende Algorithmen

Bild
Diese Veröffentlichung beschreibt die einfachsten Methoden zur Berechnung der Integrale von Funktionen einer Variablen in einem Segment, auch Quadraturformeln genannt. In der Regel werden diese Methoden in mathematischen Standardbibliotheken wie der GNU Scientific Library fĂŒr C, SciPy fĂŒr Python und anderen implementiert. Die Veröffentlichung soll zeigen, wie diese Methoden "unter der Haube" funktionieren, und auf einige Fragen der Genauigkeit und Leistung von Algorithmen aufmerksam machen. Ich möchte auch die Beziehung von Quadraturformeln und Methoden zur numerischen Integration gewöhnlicher Differentialgleichungen erwĂ€hnen, ĂŒber die ich eine weitere Veröffentlichung schreiben möchte.


Definition des Integrals


Integral (nach Riemann) einer Funktion f ( x ) auf dem Segment [ a ; b ] Die folgende Grenze wird genannt:


 i n t b a f ( x ) d x = l i m D e l t a x b i s 0 s u m n - 1 i = 0 f ( x i i ) ( x i + 1 - x i ) , ( 1 )       


wo  Deltax= max lbracexi+1−xi rbrace - Feinheit der Trennwand, x0=a , xn=b ,  xii - eine beliebige Nummer im Segment [xi;xi+1] .


Wenn das Integral der Funktion existiert, ist der Grenzwert unabhÀngig von der Partition gleich, wenn er nur ausreichend klein wÀre.
Bild
Die geometrische Definition ist klarer - das Integral entspricht der FlĂ€che des gekrĂŒmmten Trapezes, die durch die 0 x -Achse, den Funktionsgraphen und die Geraden x = a und x = b (ausgefĂŒllter Bereich in der Abbildung) begrenzt wird.


Quadraturformeln


Die Definition des Integrals (1) kann in der Form umgeschrieben werden


I= intbaf(x)dx ungefĂ€hrIn=(b−a) sumn−1i=0wif( xii),  (2)


wo wi - Gewichtungskoeffizienten, deren Summe gleich 1 sein sollte, und die Koeffizienten selbst - tendieren mit zunehmender Zahl zu Null n Punkte, an denen die Funktion berechnet wird.


Ausdruck (2) ist die Basis aller Quadraturformeln (d. H. Formeln zur ungefĂ€hren Berechnung des Integrals). Die Herausforderung besteht darin, Punkte auszuwĂ€hlen  lbrace xii rbrace und Gewicht wi damit sich die Summe auf der rechten Seite dem erforderlichen Integral so genau wie möglich annĂ€hert.


Rechenaufgabe


Funktionssatz f(x) fĂŒr die es einen Algorithmus zum Berechnen von Werten an jedem Punkt im Intervall gibt [a;b] (Ich meine die Punkte, die durch eine Gleitkommazahl dargestellt werden - dort gibt es keine Dirichlet-Funktionen !).


Es ist erforderlich, den ungefĂ€hren Wert des Integrals zu ermitteln  intbaf(x)dx .
Die Lösungen werden in Python 3.6 implementiert.


Verwenden Sie das Integral, um die Methoden zu ĂŒberprĂŒfen  int3/20 left[2x+ frac1 sqrtx+1/16 right]dx=17/4 .


StĂŒckweise konstante Approximation


Die ideal einfachen Quadraturformeln ergeben sich aus der Anwendung des Ausdrucks (1) "in der Stirn":


In= sumn−1i=0f( xii)(xi+1−xi)


Weil von der Methode der Division eines Segments durch Punkte  lbracexi rbrace und Punkte auswĂ€hlen  lbrace xii rbrace Der Grenzwert hĂ€ngt nicht davon ab, dann wĂ€hlen wir sie aus, damit sie bequem berechnet werden können. Beispielsweise nehmen wir die Partition einheitlich und betrachten fĂŒr die Punkte der Berechnung der Funktion die Optionen: 1)  xii=xi ;; 2)  xii=xi+1 ;; 3)  xii=(xi+xi+1)/2 .


Wir erhalten die Methoden fĂŒr linke Rechtecke, rechte Rechtecke und Rechtecke mit einem Mittelpunkt.


Implementierung
def _rectangle_rule(func, a, b, nseg, frac): """  .""" dx = 1.0 * (b - a) / nseg sum = 0.0 xstart = a + frac * dx # 0 <= frac <= 1    , #    , #     dx for i in range(npoints): sum += func(xstart + i * dx) return sum * dx def left_rectangle_rule(func, a, b, nseg): """  """ return _rectangle_rule(func, a, b, nseg, 0.0) def right_rectangle_rule(func, a, b, nseg): """  """ return _rectangle_rule(func, a, b, npoints, 1.0) def midpoint_rectangle_rule(func, a, b, nseg): """    """ return _rectangle_rule(func, a, b, nseg, 0.5) 

Um die Leistung von Quadraturformeln zu analysieren, erstellen wir eine grafische Darstellung des Fehlers in den Koordinaten. "Die Anzahl der Punkte ist die Differenz zwischen dem numerischen und dem exakten Ergebnis."


Bild


Was Sie bemerken können:


  1. Eine Formel mit einem Mittelpunkt ist viel genauer als mit einem rechten oder linken Punkt
  2. Der Fehler der Formel mit dem Mittelpunkt fÀllt schneller ab als die beiden anderen
  3. Bei einer sehr kleinen Partition nimmt der Fehler der Formel mit dem Mittelpunkt zu
    Die ersten beiden Punkte beziehen sich auf die Tatsache, dass die Formel von Rechtecken mit einem Mittelpunkt eine zweite NĂ€herungsordnung hat, d.h. |In−I|=O(1/n2) und die Formeln des rechten und linken Rechtecks ​​sind erster Ordnung, d.h. |In−I|=O(1/n) .
    Eine Zunahme des Fehlers wĂ€hrend des Schleifens des Integrationsschritts ist mit einer Zunahme des Rundungsfehlers verbunden, wenn eine große Anzahl von Termen summiert wird. Dieser Fehler wĂ€chst wie |In−I|=O(1/n) Dies ermöglicht keine Integration, um Maschinengenauigkeit zu erreichen.
    Schlussfolgerung: Die Methoden von Rechtecken mit rechten und linken Punkten weisen eine geringe Genauigkeit auf, die mit der Verfeinerung der Partition ebenfalls langsam zunimmt. Daher sind sie nur zu Demonstrationszwecken sinnvoll. Die Methode von Rechtecken mit einem Mittelpunkt hat eine höhere Approximationsreihenfolge, wodurch sie in realen Anwendungen verwendet werden kann (mehr dazu weiter unten).

StĂŒckweise lineare Approximation


Der nÀchste logische Schritt besteht darin, die integrierbare Funktion in jedem der Untersegmente durch eine lineare Funktion zu approximieren, die die Quadraturformel der Trapezien ergibt:


In= sumn−1i=0 fracf(xi)+f(xi+1)2(xi+1−xi)  (3)


Bild
Darstellung der Trapezmethode fĂŒr n = 1 und n = 2.


Bei einem einheitlichen Raster sind die LĂ€ngen aller Segmente der Partition gleich und die Formel hat die Form


In=h left( fracf(a)+f(b)2+ sumn−1i=1f(a+ih) right), h= fracban  (3a)


Implementierung
 def trapezoid_rule(func, a, b, nseg): """  nseg -  ,    [a;b]""" dx = 1.0 * (b - a) / nseg sum = 0.5 * (func(a) + func(b)) for i in range(1, nseg): sum += func(a + i * dx) return sum * dx 

Nachdem wir den Fehler gegen die Anzahl der Teilungspunkte aufgetragen haben, sehen wir, dass die Trapezmethode auch eine zweite NĂ€herungsordnung aufweist und im Allgemeinen geringfĂŒgig andere Ergebnisse als die Mittelpunkt-Rechteck-Methode (im Folgenden einfach die Rechteck-Methode) liefert.
Bild


Kontrolle der Berechnungsgenauigkeit


Das Einstellen der Anzahl der Teilungspunkte als Eingabeparameter ist nicht sehr praktisch, da das Integral normalerweise nicht mit einer bestimmten Partitionsdichte, sondern mit einem bestimmten Fehler berechnet werden muss. Wenn der Integrand im Voraus bekannt ist, können wir den Fehler im Voraus abschÀtzen und einen Integrationsschritt so wÀhlen, dass die angegebene Genauigkeit mit Sicherheit erreicht wird. Dies ist jedoch in der Praxis selten der Fall (und ist es im Allgemeinen mit der im Voraus bekannten Funktion nicht einfacher, das Integral im Voraus zu integrieren?). Daher ist ein Verfahren zum automatischen Anpassen des Schritts an einen bestimmten Fehler erforderlich.


Wie implementiere ich das? Eine der einfachen Methoden zur SchĂ€tzung des Fehlers - die Runge-Regel - die Differenz der Werte der Integrale, berechnet aus n und 2 n Punkten, ergibt eine FehlerschĂ€tzung:  Delta2n approx|I2n−In| . Die Trapezmethode ist bequemer, um die Feinheit einer Trennwand zu verdoppeln, als die Methode von Rechtecken mit einem Mittelpunkt. Bei der Berechnung nach der Trapezmethode werden neue Werte der Funktion nur in der Mitte der Segmente der vorherigen Partition benötigt, um die Anzahl der Punkte zu verdoppeln, d. H. Die vorherige NĂ€herung des Integrals kann verwendet werden, um die nĂ€chste zu berechnen.


WofĂŒr ist die Rechteckmethode sonst noch gut?

Bei der Rechteckmethode mĂŒssen die Werte der Funktion an den Enden des Segments nicht berechnet werden. Dies bedeutet, dass es fĂŒr Funktionen verwendet werden kann, die integrierbare Merkmale an den RĂ€ndern des Segments aufweisen (z. B. sin x / x oder x -1/2 von 0 bis 1). Daher funktioniert die unten gezeigte Extrapolationsmethode fĂŒr die Rechteckmethode genauso. Der Unterschied zur Trapezmethode besteht nur darin, dass bei Halbierung des Schritts das Ergebnis vorheriger Berechnungen verworfen wird. Sie können jedoch die Anzahl der Punkte verdreifachen und dann den vorherigen Wert des Integrals auch zur Berechnung eines neuen verwenden. Die Formeln fĂŒr die Extrapolation mĂŒssen in diesem Fall auf ein anderes VerhĂ€ltnis der Integrationsschritte eingestellt werden.


Von hier erhalten wir den folgenden Code fĂŒr die Trapezmethode mit PrĂ€zisionskontrolle:


 def trapezoid_rule(func, a, b, rtol = 1e-8, nseg0 = 1): """  rtol -     nseg0 -    """ nseg = nseg0 old_ans = 0.0 dx = 1.0 * (b - a) / nseg ans = 0.5 * (func(a) + func(b)) for i in range(1, nseg): ans += func(a + i * dx) ans *= dx err_est = max(1, abs(ans)) while (err_est > abs(rtol * ans)): old_ans = ans ans = 0.5 * (ans + midpoint_rectangle_rule(func, a, b, nseg)) #      #       nseg *= 2 err_est = abs(ans - old_ans) return ans 

Bei diesem Ansatz wird der Integrand nicht mehrmals an einem Punkt berechnet, und alle berechneten Werte werden fĂŒr das Endergebnis verwendet.


Aber ist es möglich, mit der gleichen Anzahl von Funktionsberechnungen eine höhere Genauigkeit zu erzielen? Es stellt sich heraus, dass es möglich ist, dass es Formeln gibt, die genauer als die Trapezmethode auf demselben Gitter funktionieren.


StĂŒckweise parabolische Approximation


Der nÀchste Schritt besteht darin, die Funktion mit parabolischen Elementen zu approximieren. Dies erfordert, dass die Anzahl der Segmente der Partition gerade ist, dann können Parabeln durch Dreifachpunkte mit Abszissen gezogen werden {( x 0 = a , x 1 , x 2 ), ( x 2 , x 3 , x 4 ), ..., ( x n -2 , x n -1 , x n = b )}.



Darstellung einer stĂŒckweise parabolischen NĂ€herung an 3 und 5 Punkten ( n = 2 und n = 3).


AnnĂ€herung an das Integral der Funktion in jedem der Segmente [ x k ; x k +2 ] durch das Integral der parabolischen Approximation auf diesem Segment und unter der Annahme, dass die Punkte gleichmĂ€ĂŸig verteilt sind ( x k + 1 = x k + h ), erhalten wir die Simpson-Formel :


ISimps,n= sumn/2−1i=0 frach3[f(x2i)+4f(x2i+1)+f(x2i+2)]== frach3[f(a)+4f(a+h)+2f(a+2h)+...+4f(bh)+f(b)]  (4)


Formel (4) liefert direkt eine "naive" Implementierung der Simpson-Methode:


Spoiler Überschrift
 def simpson_rule(func, a, b, nseg): """  nseg -  ,    [a;b]""" if nseg%2 = 1: nseg += 1 dx = 1.0 * (b - a) / nseg sum = (func(a) + 4 * func(a + dx) + func(b)) for i in range(1, nseg / 2): sum += 2 * func(a + (2 * i) * dx) + 4 * func(a + (2 * i + 1) * dx) return sum * dx / 3 

Um den Fehler abzuschÀtzen, können Sie dieselbe Berechnung des Integrals mit den Schritten h und h / 2 verwenden. Hier ist jedoch das Problem: Wenn Sie das Integral mit einem kleineren Schritt berechnen, muss das Ergebnis der vorherigen Berechnung verworfen werden, obwohl die HÀlfte der neuen Funktionsberechnungen an denselben Punkten wie zuvor liegt.


GlĂŒcklicherweise können Sie vermeiden, Maschinenzeit zu verschwenden, wenn Sie die Simpson-Methode auf raffiniertere Weise implementieren. Bei nĂ€herer Betrachtung stellen wir fest, dass das Integral durch die Simpson-Formel durch zwei Integrale durch die Trapezformel mit unterschiedlichen Schritten dargestellt werden kann. Dies zeigt sich am deutlichsten im Grundfall der Approximation des Integrals ĂŒber drei Punkte (a,f0), (a+h,f1), (a+2h,f2) ::


ISimps,2= frach3(f0+4f1+f2)= frac43h left( fracf0+f12+ fracf1+f22 rechts)− frac13 cdot2h fracf0+f22== frac4Itrap,2−Itrap,13


Wenn wir also das Verfahren zur Halbierungsreduzierung implementieren und die letzten beiden Berechnungen nach der Trapezmethode speichern, wird die Simpson-Methode mit Genauigkeitskontrolle effizienter implementiert.


Irgendwie so...
 class Quadrature: """    """ __sum = 0.0 __nseg = 1 #    __ncalls = 0 #      def __restart(func, x0, x1, nseg0, reset_calls = True): """    .       """ if reset_calls: Quadrature.__ncalls = 0 Quadrature.__nseg = nseg0 #           nseg0 Quadrature.__sum = 0.5 * (func(x0) + func(x1)) dx = 1.0 * (x1 - x0) / nseg0 for i in range(1, nseg0): Quadrature.__sum += func(x0 + i * dx) Quadrature.__ncalls += 1 + nseg0 return Quadrature.__sum * dx def __double_nseg(func, x0, x1): """  .       """ nseg = Quadrature.__nseg dx = (x1 - x0) / nseg x = x0 + 0.5 * dx i = 0 AddedSum = 0.0 for i in range(nseg): AddedSum += func(x + i * dx) Quadrature.__sum += AddedSum Quadrature.__nseg *= 2 Quadrature.__ncalls += nseg return Quadrature.__sum * 0.5 * dx def trapezoid(func, x0, x1, rtol = 1e-10, nseg0 = 1): """     . rtol -  , nseg0 -    """ ans = Quadrature.__restart(func, x0, x1, nseg0) old_ans = 0.0 err_est = max(1, abs(ans)) while (err_est > abs(rtol * ans)): old_ans = ans ans = Quadrature.__double_nseg(func, x0, x1) err_est = abs(old_ans - ans) print("Total function calls: " + str(Quadrature.__ncalls)) return ans def simpson(func, x0, x1, rtol = 1.0e-10, nseg0 = 1): """     . rtol -  , nseg0 -    """ old_trapez_sum = Quadrature.__restart(func, x0, x1, nseg0) new_trapez_sum = Quadrature.__double_nseg(func, x0, x1) ans = (4 * new_trapez_sum - old_trapez_sum) / 3 old_ans = 0.0 err_est = max(1, abs(ans)) while (err_est > abs(rtol * ans)): old_ans = ans old_trapez_sum = new_trapez_sum new_trapez_sum = Quadrature.__double_nseg(func, x0, x1) ans = (4 * new_trapez_sum - old_trapez_sum) / 3 err_est = abs(old_ans - ans) print("Total function calls: " + str(Quadrature.__ncalls)) return ans 

Vergleichen Sie die Wirksamkeit der Trapez- und Parabelmethode:


 >>> import math >>> Quadrature.trapezoid(lambda x: 2 * x + 1 / math.sqrt(x + 1 / 16), 0, 1.5, rtol=1e-9) Total function calls: 65537 4.250000001385811 >>> Quadrature.simpson(lambda x: 2 * x + 1 / math.sqrt(x + 1 / 16), 0, 1.5, rtol=1e-9) Total function calls: 2049 4.2500000000490985 

Wie Sie sehen können, kann mit beiden Methoden die Antwort mit ziemlich hoher Genauigkeit erhalten werden, aber die Anzahl der Aufrufe des Integranden ist sehr unterschiedlich - eine Methode höherer Ordnung ist 32-mal effizienter!


Durch Auftragen des Integrationsfehlers gegen die Anzahl der Schritte können wir ĂŒberprĂŒfen, ob die Approximationsreihenfolge der Simpson-Formel vier ist, d. H. numerischer Integrationsfehler |ISimps,n−I|=O(1/n4) (und die Integrale kubischer Polynome unter Verwendung dieser Formel werden bis zu Rundungsfehlern fĂŒr jedes gerade n > 0 berechnet!).
Bild
Daher ergibt sich eine solche Effizienzsteigerung im Vergleich zur einfachen Trapezformel.


Was weiter?


Die weitere Logik, die Genauigkeit von Quadraturformeln zu erhöhen, ist allgemein verstĂ€ndlich. Wenn wir die Funktion weiterhin mit Polynomen von immer höherem Grad approximieren, wird das Integral dieser Polynome das Integral der ursprĂŒnglichen Funktion immer genauer approximieren. Dieser Ansatz wird als Konstruktion quadratischer Newton-Cotes-Formeln bezeichnet . Formeln bis zu 8 Approximationsordnungen sind bekannt, aber die alternierenden Terme erscheinen unter den Gewichtungskoeffizienten w i in (2), und die Formeln verlieren bei den Berechnungen an StabilitĂ€t.


Versuchen wir es andersherum. Der Fehler der Quadraturformel wird als Potenzreihe des Integrationsschritts h dargestellt . Eine bemerkenswerte Eigenschaft der Trapezmethode (und Rechtecke mit einem Mittelpunkt!) Ist, dass diese Reihe dafĂŒr nur aus geraden Graden besteht:


Itrap,n[f,a,b]= intbaf(x)dx+C2h2+C4h4+C6h6+..., h= fracban   (5)


Die Richardson-Extrapolation basiert darauf, sukzessive Approximationen fĂŒr diese Erweiterung zu finden: Anstatt den Integranden durch ein Polynom aus den berechneten Approximationen des Integrals zu approximieren I(h) Es wird eine PolynomnĂ€herung konstruiert, die fĂŒr h = 0 die beste AnnĂ€herung an den wahren Wert des Integrals ergeben sollte.


Das Erweitern des Integrationsfehlers in geraden Potenzen des Partitionsschritts beschleunigt die Konvergenz der Extrapolation stark, weil Zur Approximation der Ordnung 2 n werden nach der Trapezmethode nur n Werte des Integrals benötigt.


Wenn wir annehmen, dass jeder nachfolgende Term kleiner als der vorherige ist, können wir nacheinander Grad von h ausschließen , wobei integrale Approximationen mit verschiedenen Schritten berechnet werden. Da die obige Implementierung es uns leicht ermöglicht, die Partition in zwei HĂ€lften zu teilen, ist es zweckmĂ€ĂŸig, die Formeln fĂŒr die Schritte h und h / 2 zu berĂŒcksichtigen.


Itrap,n−I ungefĂ€hrC2h2; Itrap,2n−I ungefĂ€hrC2 left( frach2 right)2


Es ist leicht zu zeigen, dass die Ausnahme des Àlteren Ausdrucks des Fehlers der Trapezformel genau die Simpson-Formel ergibt:


I=ITrap,2n−C2 left( frach2 right)2+O(h4) ca.Itrap,2n− fracItrap,2n−Itrap,n1−22=ISimps,2n


Wenn wir ein Ă€hnliches Verfahren fĂŒr die Simpson-Formel wiederholen, erhalten wir:


ISimps,2n−I ungefĂ€hrC4 left( frach2 right)4; ISimps,n−I ungefĂ€hrC4h4


I=ISimps,2n−C4 left( frach2 right)4+O(h6) ca.ISimps,2n− fracISimps,2n−ISimps,n1−24


Wenn Sie fortfahren, wird die folgende Tabelle angezeigt:


2 bestellen4 bestellen6 bestellen...
I 0,0
Ich 1,0I 1,1
I 2.0I 2.1I 2.2
.........

Die erste Spalte enthÀlt die nach der Trapezmethode berechneten Integrale. Wenn Sie sich von der oberen Reihe nach unten bewegen, wird die Teilung des Segments doppelt so klein, und wenn Sie sich von der linken Spalte nach rechts bewegen, nimmt die Approximationsreihenfolge des Integrals zu (d. H. Die zweite Spalte enthÀlt die Integrale nach der Simpson-Methode usw.).


Die Elemente der Tabelle, wie aus der Erweiterung (5) abgeleitet werden kann, sind durch die Wiederholungsrelation verbunden:


Ii,j=Ii,j−1− fracIi,j−1−Ii−1,j−11− left( frachijhi right)2=Ii,j−1− fracIi,j−1−Ii−1,j−11−22j   (6)


Der Fehler der Approximation des Integrals kann aus der Differenz von Formeln unterschiedlicher Ordnung in einer Zeile geschÀtzt werden, d.h.


 Deltai,j ungefĂ€hrIi,j−Ii,j−1


Die Verwendung der Richardson-Extrapolation zusammen mit der Trapezintegration wird als Romberg-Methode bezeichnet . Wenn die Simpson-Methode die beiden vorherigen Werte nach der Trapezmethode berĂŒcksichtigt, verwendet die Romberg-Methode alle zuvor nach der Trapezmethode berechneten Werte, um eine genauere SchĂ€tzung des Integrals zu erhalten.


Implementierung

Der Quadraturklasse wird eine zusĂ€tzliche Methode hinzugefĂŒgt


 class Quadrature: """    """ __sum = 0.0 __nseg = 1 #    __ncalls = 0 #      def __restart(func, x0, x1, nseg0, reset_calls = True): """    .       """ if reset_calls: Quadrature.__ncalls = 0 Quadrature.__nseg = nseg0 #          nseg0  Quadrature.__sum = 0.5 * (func(x0) + func(x1)) dx = 1.0 * (x1 - x0) / nseg0 for i in range(1, nseg0): Quadrature.__sum += func(x0 + i * dx) Quadrature.__ncalls += 1 + nseg0 return Quadrature.__sum * dx def __double_nseg(func, x0, x1): """  .       """ nseg = Quadrature.__nseg dx = (x1 - x0) / nseg x = x0 + 0.5 * dx i = 0 AddedSum = 0.0 for i in range(nseg): AddedSum += func(x + i * dx) Quadrature.__sum += AddedSum Quadrature.__nseg *= 2 Quadrature.__ncalls += nseg return Quadrature.__sum * 0.5 * dx def romberg(func, x0, x1, rtol = 1e-10, nseg0 = 1, maxcol = 5, reset_calls = True): """   nseg0 -     maxcol -   """ #   Itable = [[Quadrature.__restart(func, x0, x1, nseg0, reset_calls)]] i = 0 maxcol = max(0, maxcol) ans = Itable[i][i] error_est = max(1, abs(ans)) while (error_est > abs(rtol * ans)): old_ans = ans i += 1 d = 4.0 ans_col = min(i, maxcol) Itable.append([Quadrature.__double_nseg(func, x0, x1)] * (ans_col + 1)) for j in range(0, ans_col): diff = Itable[i][j] - Itable[i - 1][j] Itable[i][j + 1] = Itable[i][j] + diff / (d - 1.0) d *= 4.0 ans = Itable[i][ans_col] if (maxcol <= 1): #       error_est = abs(ans - Itable[i - 1][-1]) elif (i > maxcol): error_est = abs(ans - Itable[i][min(i - maxcol - 1, maxcol - 1)]) else: error_est = abs(ans - Itable[i - 1][i - 1]) print("Total function calls: " + str(Quadrature.__ncalls)) return ans 

Lassen Sie uns ĂŒberprĂŒfen, wie die NĂ€herung höherer Ordnung funktioniert:


 >>> Quadrature.romberg(lambda x: 2 * x + 1 / math.sqrt(x + 1/16), 0, 1.5, rtol=1e-9, maxcol = 0) #  Total function calls: 65537 4.250000001385811 >>> Quadrature.romberg(lambda x: 2 * x + 1 / math.sqrt(x + 1/16), 0, 1.5, rtol=1e-9, maxcol = 1) #  Total function calls: 2049 4.2500000000490985 >>> Quadrature.romberg(lambda x: 2 * x + 1 / math.sqrt(x + 1/16), 0, 1.5, rtol=1e-9, maxcol = 4) Total function calls: 257 4.250000001644076 

Wir sind davon ĂŒberzeugt, dass sich die Anzahl der Aufrufe des Integranden im Vergleich zur Parabelmethode um das Achtfache verringert hat. Mit einer weiteren Erhöhung der erforderlichen Genauigkeit werden die Vorteile der Romberg-Methode noch deutlicher:
Bild


Einige Notizen


Bemerkung 1. Die Anzahl der Funktionsaufrufe bei diesen Problemen kennzeichnet die Anzahl der Summen bei der Berechnung des Integrals. Das Reduzieren der Anzahl von Berechnungen des Integranden spart nicht nur Rechenressourcen (obwohl dies auch bei einer optimierten Implementierung der Fall ist), sondern verringert auch die Auswirkung von Rundungsfehlern auf das Ergebnis. Wenn also versucht wird, das Integral der Testfunktion zu berechnen, friert die Trapezmethode ein, wenn versucht wird, eine relative Genauigkeit von 5 × 10 -15 zu erreichen , die Parabelmethode - mit der gewĂŒnschten Genauigkeit von 2 × 10 -16 (was die Grenze fĂŒr Zahlen mit doppelter Genauigkeit ist), und die Romberg-Methode bewĂ€ltigt die Berechnung Testintegral bis auf Maschinengenauigkeit (mit geringem Bitfehler). Das heißt, nicht nur die Genauigkeit der Integration wird fĂŒr eine gegebene Anzahl von Funktionsaufrufen erhöht, sondern auch die maximal erreichbare Genauigkeit der Berechnung des Integrals.


Anmerkung 2. Wenn die Methode konvergiert, wenn eine bestimmte Genauigkeit angegeben wird, bedeutet dies nicht, dass der berechnete Wert des Integrals dieselbe Genauigkeit aufweist. Dies gilt zunĂ€chst fĂŒr FĂ€lle, in denen der angegebene Fehler nahe an der Maschinengenauigkeit liegt.


Bemerkung 3. Obwohl die Romberg-Methode fĂŒr eine Reihe von Funktionen auf fast magische Weise funktioniert, wird davon ausgegangen, dass der Integrand Ableitungen hoher Ordnung begrenzt hat. Dies bedeutet, dass sich Funktionen mit Knicken oder Unterbrechungen als schlechter als einfache Methoden herausstellen können. Integrieren Sie beispielsweise f ( x ) = | x |:


 >>> Quadrature.trapezoid(abs, -1, 3, rtol=1e-5) Total function calls: 9 5.0 >>> Quadrature.simpson(abs, -1, 3, rtol=1e-5) Total function calls: 17 5.0 >>> Quadrature.romberg(abs, -1, 3, rtol=1e-5, maxcol = 2) Total function calls: 17 5.0 >>> Quadrature.romberg(abs, -1, 3, rtol=1e-5, maxcol = 3) Total function calls: 33 5.0 >>> Quadrature.romberg(abs, -1, 3, rtol=1e-5, maxcol = 4) Total function calls: 33 5.000001383269357 

Bemerkung 4. Je höher die Approximationsreihenfolge, desto besser. In der Tat ist es besser, die Anzahl der Spalten in der Romberg-Tabelle auf 4-6 zu begrenzen. Um dies zu verstehen, schauen Sie sich Formel (6) an. Der zweite Term ist die Differenz zweier aufeinanderfolgender Elemente der j-1- ten Spalte geteilt durch etwa 4 j . Weil Die j- te Spalte enthĂ€lt NĂ€herungen eines Integrals der Ordnung 2j , dann liegt die Differenz selbst in der GrĂ¶ĂŸenordnung von (1 / n i ) 2 j ~ 4 - ij . Unter BerĂŒcksichtigung der Division wird ~ 4 - ( i + 1) j ~ 4 - j 2 erhalten. Das heißt, fĂŒr j ~ 7 verliert der zweite Term in (6) nach der Reduzierung der Ordnungen beim HinzufĂŒgen von Gleitkommazahlen an Genauigkeit, und eine Erhöhung der Approximationsreihenfolge kann zur Akkumulation von Rundungsfehlern fĂŒhren.


Bemerkung 5. Interessenten können die beschriebenen Methoden verwenden, um das Integral aus GrĂŒnden des Interesses zu finden.  int10 sqrtx sinxdx und gleichwertig mit ihm  int102t2 sint2dt . Wie sie sagen, fĂŒhle den Unterschied.


Fazit


Die Beschreibung und Implementierung der grundlegenden Methoden zur numerischen Integration von Funktionen in einem einheitlichen Raster wird vorgestellt. Es wird gezeigt, wie mit einer einfachen Modifikation die Klasse der Quadraturformeln unter Verwendung der Romberg-Methode auf der Basis der Trapezmethode erhalten werden kann, was die Konvergenz der numerischen Integration erheblich beschleunigt. Das Verfahren funktioniert gut zum Integrieren von "gewöhnlichen" Funktionen, d.h. schwach variierend im Integrationsintervall, ohne SingularitÀten an den RÀndern des Segments (siehe Bemerkung 5), schnelle Schwingungen usw.


( [3] — C++).


Literatur


  1. .. , .. . . .: . 1989.
  2. J. Stoer, R. Bulirsch. Introduction to Numerical Analysis: Second Edition. Springer-Verlag New York. 1993.
  3. WH Press, SA Teukolsky, WT Vetterling, BP Flannery. Numerical Recipes: Third Edition. Cambridge University Press. 2007.

Source: https://habr.com/ru/post/de420867/


All Articles