double trap(double func(double), double b, double a, double N) {
double j;
double s;
double h = (b-a)/(N-1.0); //Width of trapezia
double func1 = func(a);
double func2;
for (s=0,j=a;j<b;j+=h){
func2 = func(j+h);
s = s + 0.5*(func1+func2)*h;
func1 = func2;
}
return s;
}
Das Obige ist mein C ++ - Code für eine 1D-numerische Integration (unter Verwendung der erweiterten Trapezregel) func()
zwischen den Grenzwerten Verwendung von Trapez .N - 1
Ich mache gerade eine 3D-Integration, bei der dieser Code rekursiv aufgerufen wird. Ich arbeite mit und erhalte gute Ergebnisse.
Kann jemand außer der weiteren Reduzierung von vorschlagen, wie der obige Code so optimiert werden kann, dass er schneller ausgeführt wird? Oder können Sie sogar eine schnellere Integrationsmethode vorschlagen?
c++
performance
user2970116
quelle
quelle
trapezoidal_integration
statttrap
,sum
oderrunning_total
anstelle vons
(und auch verwenden+=
statts = s +
),trapezoid_width
oderdx
anstelle vonh
(oder auch nicht, je nach Ihrer bevorzugten Schreibweise für die Trapezregel), und ändernfunc1
und umfunc2
die Tatsache widerzuspiegeln , dass sie Werte sind, nicht funktioniert. ZBfunc1
->previous_value
undfunc2
->current_value
oder so ähnlich.Antworten:
Mathematisch entspricht Ihr Ausdruck:
Das könnten Sie also umsetzen. Wie bereits erwähnt, wird die Zeit wahrscheinlich von der Funktionsbewertung dominiert. Um die gleiche Genauigkeit zu erzielen, können Sie eine bessere Integrationsmethode verwenden, die weniger Funktionsbewertungen erfordert.
Die Gaußsche Quadratur ist heutzutage etwas mehr als ein Spielzeug. Nur nützlich, wenn Sie nur sehr wenige Bewertungen benötigen . Wenn Sie etwas einfach zu implementieren möchten, können Sie die Simpson-Regel verwenden, aber ich würde nicht ohne Grund weiter gehen als bestellen .1 / N.3
Wenn sich die Krümmung der Funktion stark ändert, können Sie eine adaptive Schrittroutine verwenden, die einen größeren Schritt auswählt, wenn die Funktion flach ist, und einen kleineren, genaueren Schritt, wenn die Krümmung höher ist.
quelle
Wahrscheinlich ist die Bewertung der Funktionen der zeitaufwändigste Teil dieser Berechnung. Wenn dies der Fall ist, sollten Sie sich darauf konzentrieren, die Geschwindigkeit von func () zu verbessern, anstatt zu versuchen, die Integrationsroutine selbst zu beschleunigen.
Abhängig von den Eigenschaften von func () ist es auch wahrscheinlich, dass Sie mithilfe einer komplexeren Integrationsformel eine genauere Bewertung des Integrals mit weniger Funktionsbewertungen erhalten.
quelle
Möglich? Ja. Nützlich? Nein. Es ist unwahrscheinlich, dass die Optimierungen, die ich hier auflisten werde, mehr als einen winzigen Bruchteil eines Prozentunterschieds in der Laufzeit ausmachen. Ein guter Compiler kann dies bereits für Sie tun.
Wie auch immer, wenn Sie Ihre innere Schleife betrachten:
Bei jeder Schleifeniteration führen Sie drei mathematische Operationen aus, die nach außen gebracht werden können: Addieren
j + h
, Multiplizieren mit0.5
und Multiplizieren mith
. Das erste können Sie beheben, indem Sie Ihre Iteratorvariable bei startena + h
, und das andere, indem Sie die Multiplikationen herausrechnen:Obwohl ich darauf hinweisen möchte, dass auf diese Weise aufgrund eines Gleitkomma-Rundungsfehlers die letzte Iteration der Schleife übersehen werden kann. (Dies war auch ein Problem in Ihrer ursprünglichen Implementierung.) Um dies zu umgehen, verwenden Sie einen
unsigned int
oder einensize_t
Zähler:Wie Brians Antwort sagt, ist es besser, Ihre Zeit damit zu verbringen, die Bewertung der Funktion zu optimieren
func
. Wenn die Genauigkeit dieser Methode ausreicht, werden Sie wahrscheinlich nichts schnelleres findenN
. (Sie könnten jedoch einige Tests durchführen, um festzustellen, ob Sie beispielsweise mit Runge-Kutta so weit abgesenkt werden könnenN
, dass die Gesamtintegration weniger Zeit in Anspruch nimmt, ohne die Genauigkeit zu beeinträchtigen.)quelle
Es gibt verschiedene Änderungen, die ich empfehlen würde, um die Berechnung zu verbessern:
std::fma()
Gründen der Leistung und Genauigkeit eine fusionierte Multiplikationsaddition .h
, da dies zu Rundungsfehlern führen kann.Darüber hinaus würde ich aus Gründen der Klarheit einige Änderungen vornehmen:
a
undb
die Funktionssignatur.N
→n
,h
→dx
,j
→x2
,s
→accumulator
.n
zu einemint
.quelle
Wenn Ihre Funktion ein Polynom ist, das möglicherweise durch eine Funktion gewichtet wird (z. B. ein Gaußscher Wert), können Sie eine genaue Integration in 3D direkt mit einer Kubaturformel durchführen (z. B. http://people.sc.fsu.edu/~jburkardt/c_src/). stroud / stroud.html ) oder mit einem spärlichen Raster (zB http://tasmanian.ornl.gov/ ). Diese Methoden geben einfach eine Reihe von Punkten und Gewichten an, um den Funktionswert mit zu multiplizieren, sodass sie sehr schnell sind. Wenn Ihre Funktion glatt genug ist, um durch Polynome angenähert zu werden, können diese Methoden immer noch eine sehr gute Antwort geben. Die Formeln sind auf die Art der Funktion spezialisiert, die Sie integrieren. Daher kann es einige Zeit dauern, bis Sie die richtige gefunden haben.
quelle
Wenn Sie versuchen, ein Integral numerisch zu berechnen, versuchen Sie, die gewünschte Präzision mit dem geringstmöglichen Aufwand zu erzielen, oder versuchen Sie alternativ, mit einem festen Aufwand die höchstmögliche Präzision zu erzielen. Sie scheinen sich zu fragen, wie der Code für einen bestimmten Algorithmus so schnell wie möglich ausgeführt werden kann.
Das mag Ihnen einen kleinen Gewinn bringen, aber es wird wenig sein. Es gibt viel effizientere Methoden zur numerischen Integration. Google für "Simpsons Regel", "Runge-Kutta" und "Fehlberg". Sie funktionieren alle ziemlich ähnlich, indem sie einige Werte der Funktion auswerten und ein Vielfaches dieser Werte geschickt addieren, wodurch viel kleinere Fehler mit der gleichen Anzahl von Funktionsauswertungen oder der gleiche Fehler mit einer viel geringeren Anzahl von Auswertungen erzeugt werden.
quelle
Es gibt viele Möglichkeiten zur Integration, von denen die Trapezregel die einfachste ist.
Wenn Sie überhaupt etwas über die tatsächliche Funktion wissen, die Sie integrieren, können Sie es besser machen, wenn Sie dies ausnutzen. Die Idee ist, die Anzahl der Gitterpunkte innerhalb akzeptabler Fehlerstufen zu minimieren.
Zum Beispiel passt Trapez linear an aufeinanderfolgende Punkte an. Sie könnten eine quadratische Anpassung vornehmen, die bei glatter Kurve besser passt, sodass Sie ein gröberes Gitter verwenden können.
Orbitalsimulationen werden manchmal mit Kegeln durchgeführt, da Bahnen Kegelschnitten sehr ähnlich sind.
In meiner Arbeit integrieren wir Formen, die sich glockenförmigen Kurven annähern, so dass es effektiv ist, sie als solche zu modellieren (die adaptive Gaußsche Quadratur wird in dieser Arbeit als "Goldstandard" angesehen).
quelle
Wie bereits in anderen Antworten erwähnt, hängt dies stark davon ab, wie teuer Ihre Funktion ist. Die Optimierung Ihres Trapz-Codes lohnt sich nur, wenn es wirklich Ihr Engpass ist. Wenn dies nicht ganz offensichtlich ist, sollten Sie dies überprüfen, indem Sie Ihren Code profilieren (Tools wie Intel V-Tune, Valgrind oder Visual Studio können dies tun).
Ich würde jedoch einen völlig anderen Ansatz vorschlagen: die Monte-Carlo-Integration . Hier approximieren Sie einfach das Integral, indem Sie Ihre Funktion an zufälligen Punkten abtasten und die Ergebnisse hinzufügen. Weitere Informationen finden Sie in diesem PDF zusätzlich zur Wiki-Seite.
Dies funktioniert sehr gut für hochdimensionale Daten, typischerweise viel besser als die Quadraturmethoden, die bei der 1-D-Integration verwendet werden.
Der einfache Fall ist sehr einfach zu implementieren (siehe PDF). Achten Sie jedoch darauf, dass die Standard-Zufallsfunktion in c ++ 98 sowohl hinsichtlich der Leistung als auch der Qualität ziemlich schlecht ist. In c ++ 11 können Sie den Mersenne Twister in verwenden.
Wenn Ihre Funktion in einigen Bereichen stark variiert und in anderen weniger, sollten Sie die Verwendung einer geschichteten Stichprobe in Betracht ziehen. Ich würde empfehlen, die wissenschaftliche Bibliothek der GNU zu verwenden , anstatt Ihre eigene zu schreiben.
quelle
"rekursiv" ist der Schlüssel. Sie durchlaufen entweder einen großen Datensatz und berücksichtigen viele Daten mehr als einmal, oder Sie generieren Ihren Datensatz tatsächlich selbst aus (stückweisen?) Funktionen.
Rekursiv bewertete Integrationen sind lächerlich teuer und lächerlich ungenau, wenn die Potenzen in der Rekursion zunehmen.
Erstellen Sie ein Modell für die Interpolation Ihres Datensatzes und führen Sie eine stückweise symbolische Integration durch. Da dann viele Daten zu Koeffizienten von Basisfunktionen zusammenfallen, wächst die Komplexität für eine tiefere Rekursion eher polynomiell (und normalerweise eher gering) als exponentiell. Und Sie erhalten "genaue" Ergebnisse (Sie müssen immer noch gute Bewertungsschemata finden, um eine angemessene numerische Leistung zu erzielen, aber es sollte immer noch machbar sein, eine bessere als die trapezförmige Integration zu erzielen).
Wenn Sie sich die Fehlerschätzungen für trapezförmige Regeln ansehen, werden Sie feststellen, dass sie mit einer Ableitung der beteiligten Funktionen zusammenhängen, und wenn die Integration / Definition rekursiv erfolgt, haben die Funktionen keine gut verhaltenen Ableitungen .
Wenn Ihr einziges Werkzeug ein Hammer ist, sieht jedes Problem wie ein Nagel aus. Während Sie das Problem in Ihrer Beschreibung kaum ansprechen, habe ich den Verdacht, dass die rekursive Anwendung der Trapezregel eine schlechte Übereinstimmung ist: Sie erhalten eine Explosion sowohl von Ungenauigkeiten als auch von Rechenanforderungen.
quelle
quelle