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;
}
Ce qui précède est mon code C ++ pour une intégration numérique 1D (en utilisant la règle du trapèze étendu) func()
entre les limites utilisant le trapèze .N - 1
Je fais actuellement une intégration 3D, où ce code est appelé récursivement. Je travaille avec ce qui me donne des résultats décents.
À part réduire davantage , quelqu'un peut-il suggérer comment optimiser le code ci-dessus pour qu'il s'exécute plus rapidement? Ou, même, peut suggérer une méthode d'intégration plus rapide?
c++
performance
user2970116
la source
la source
trapezoidal_integration
au lieu detrap
,sum
ourunning_total
au lieu des
(et également utiliser à la+=
place des = s +
),trapezoid_width
oudx
au lieu deh
(ou non, selon votre notation préférée pour la règle trapézoïdale), et changezfunc1
etfunc2
pour refléter le fait que ce sont des valeurs, pas des fonctions. Par exemplefunc1
->previous_value
etfunc2
->current_value
, ou quelque chose comme ça.Réponses:
Mathématiquement, votre expression équivaut à:
Vous pouvez donc mettre cela en œuvre. Comme il a été dit, le temps est probablement dominé par l'évaluation de la fonction, donc pour obtenir la même précision, vous pouvez utiliser une meilleure méthode d'intégration qui nécessite moins d'évaluations de fonction.
La quadrature gaussienne est, de nos jours, un peu plus qu'un jouet; utile uniquement si vous avez besoin de très peu d'évaluations. Si vous voulez quelque chose de facile à implémenter, vous pouvez utiliser la règle de Simpson, mais je n'irais pas plus loin que commander sans une bonne raison.1/N3
Si la courbure de la fonction change beaucoup, vous pouvez utiliser une routine d'étape adaptative, qui sélectionnerait une étape plus grande lorsque la fonction est plate et une plus petite et plus précise lorsque la courbure est plus élevée.
la source
Il y a de fortes chances que l'évaluation des fonctions soit la partie la plus longue de ce calcul. Si c'est le cas, vous devriez vous concentrer sur l'amélioration de la vitesse de func () plutôt que d'essayer d'accélérer la routine d'intégration elle-même.
Selon les propriétés de func (), il est également probable que vous puissiez obtenir une évaluation plus précise de l'intégrale avec moins d'évaluations de fonctions en utilisant une formule d'intégration plus sophistiquée.
la source
Possible? Oui. Utile? Non. Il est peu probable que les optimisations que je vais énumérer ici fassent plus qu'une infime fraction d'un pourcentage de différence dans l'exécution. Un bon compilateur peut déjà les faire pour vous.
Quoi qu'il en soit, en regardant votre boucle intérieure:
À chaque itération de boucle, vous effectuez trois opérations mathématiques qui peuvent être apportées à l'extérieur: addition
j + h
, multiplication par0.5
et multiplication parh
. Le premier que vous pouvez corriger en démarrant votre variable d'itérateur sura + h
, et les autres en factorisant les multiplications:Bien que je souligne qu'en faisant cela, en raison d'une erreur d'arrondi en virgule flottante, il est possible de manquer la dernière itération de la boucle. (C'était également un problème dans votre implémentation d'origine.) Pour contourner ce problème, utilisez un
unsigned int
ou unsize_t
compteur:Comme le dit la réponse de Brian, votre temps est mieux utilisé pour optimiser l'évaluation de la fonction
func
. Si la précision de cette méthode est suffisante, je doute que vous trouverez quelque chose de plus rapide pour la même choseN
. (Bien que vous puissiez exécuter des tests pour voir si, par exemple, Runge-Kutta vous permet de baisserN
suffisamment pour que l'intégration globale prenne moins de temps sans sacrifier la précision.)la source
Il y a plusieurs changements que je recommanderais pour améliorer le calcul:
std::fma()
, qui effectue une multiplication-addition fusionnée .h
, qui pourrait accumuler des erreurs d'arrondi.De plus, j'apporterais plusieurs modifications pour plus de clarté:
a
etb
dans la signature de fonction.N
→n
,h
→dx
,j
→x2
,s
→accumulator
.n
à unint
.la source
Si votre fonction est un polynôme, éventuellement pondéré par une fonction (par exemple une gaussienne), vous pouvez faire une intégration exacte en 3D directement avec une formule cubature (par exemple http://people.sc.fsu.edu/~jburkardt/c_src/ stroud / stroud.html ) ou avec une grille clairsemée (par exemple http://tasmanian.ornl.gov/ ). Ces méthodes spécifient simplement un ensemble de points et de poids pour multiplier la valeur de la fonction, ils sont donc très rapides. Si votre fonction est suffisamment lisse pour être approximée par des polynômes, ces méthodes peuvent quand même donner une très bonne réponse. Les formules sont spécialisées en fonction du type de fonction que vous intégrez, il peut donc être nécessaire de creuser pour trouver la bonne.
la source
Lorsque vous essayez de calculer une intégrale numériquement, vous essayez d'obtenir la précision que vous souhaitez avec le moindre effort possible, ou bien, essayez d'obtenir la précision la plus élevée possible avec un effort fixe. Vous semblez demander comment rendre le code d'un algorithme particulier aussi rapide que possible.
Cela peut vous donner un petit gain, mais ce sera peu. Il existe des méthodes beaucoup plus efficaces pour l'intégration numérique. Google pour "la règle de Simpson", "Runge-Kutta" et "Fehlberg". Ils fonctionnent tous de manière assez similaire en évaluant certaines valeurs de la fonction et en ajoutant intelligemment des multiples de ces valeurs, produisant des erreurs beaucoup plus petites avec le même nombre d'évaluations de fonction, ou la même erreur avec un nombre d'évaluations beaucoup plus petit.
la source
Il existe de nombreuses façons de faire l'intégration, dont la règle trapézoïdale est la plus simple.
Si vous savez quelque chose sur la fonction réelle que vous intégrez, vous pouvez faire mieux si vous l'exploitez. L'idée est de minimiser le nombre de points de grille dans des niveaux d'erreur acceptables.
Par exemple, trapézoïdal fait un ajustement linéaire à des points consécutifs. Vous pouvez effectuer un ajustement quadratique qui, si la courbe est lisse, conviendrait mieux, ce qui pourrait vous permettre d'utiliser une grille plus grossière.
Les simulations orbitales sont parfois effectuées à l'aide de coniques, car les orbites ressemblent beaucoup à des sections coniques.
Dans mon travail, nous intégrons des formes qui se rapprochent des courbes en forme de cloche, il est donc efficace de les modéliser comme cela ( la quadrature gaussienne adaptative est considérée comme le "gold standard" dans ce travail).
la source
Ainsi, comme cela a été souligné dans d'autres réponses, cela dépend fortement du coût de votre fonction. L'optimisation de votre code trapz ne vaut que si c'est vraiment votre goulot d'étranglement. Si ce n'est pas complètement évident, vous devriez vérifier cela en profilant votre code (des outils comme Intels V-tune, Valgrind ou Visual Studio peuvent le faire).
Je proposerais cependant une approche complètement différente: l' intégration de Monte Carlo . Ici, vous approximez simplement l'intégrale en échantillonnant votre fonction à des points aléatoires en ajoutant les résultats. Voir ce pdf en plus de la page wiki pour plus de détails.
Cela fonctionne extrêmement bien pour les données de grande dimension, généralement beaucoup mieux que les méthodes de quadrature utilisées dans l'intégration 1-d.
Le cas simple est très facile à implémenter (voir le pdf), faites juste attention à ce que la fonction aléatoire standard en c ++ 98 soit assez mauvaise en termes de performances et de qualité. En c ++ 11, vous pouvez utiliser le Mersenne Twister dans.
Si votre fonction présente beaucoup de variations dans certains domaines et moins dans d'autres, envisagez d'utiliser l'échantillonnage stratifié. Je recommanderais cependant d'utiliser la bibliothèque scientifique GNU , plutôt que d'écrire la vôtre.
la source
"récursivement" est la clé. Vous parcourez un grand ensemble de données et envisagez plusieurs fois plusieurs données, ou bien vous générez vous-même votre ensemble de données à partir de fonctions (par morceaux?).
Les intégrations évaluées de manière récursive seront ridiculement chères et ridiculement imprécises à mesure que les puissances augmentent en récursivité.
Créez un modèle pour interpoler votre ensemble de données et effectuez une intégration symbolique par morceaux. Étant donné que de nombreuses données s'effondrent ensuite en coefficients de fonctions de base, la complexité d'une récursion plus profonde croît de manière polynomiale (et généralement plutôt de faibles puissances) plutôt qu'exponentiellement. Et vous obtenez des résultats "exacts" (vous devez toujours trouver de bons schémas d'évaluation pour obtenir des performances numériques raisonnables, mais il devrait toujours être plutôt possible de faire mieux que l'intégration trapézoïdale).
Si vous regardez les estimations d'erreur pour les règles trapézoïdales, vous constaterez qu'elles sont liées à une dérivée des fonctions impliquées, et si l'intégration / la définition est récursive, les fonctions n'auront pas tendance à avoir des dérivées bien comportées. .
Si votre seul outil est un marteau, chaque problème ressemble à un clou. Alors que vous abordez à peine le problème dans votre description, j'ai le soupçon que l'application récursive de la règle trapézoïdale est une mauvaise correspondance: vous obtenez une explosion d'exigences d'inexactitude et de calcul.
la source
la source