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;
}
Lo anterior es mi código C ++ para una integración numérica 1D (usando la regla de trapecio extendida) de func()
entre límites usando trapecia .
Realmente estoy haciendo una integración 3D, donde este código se llama recursivamente. Trabajo con dándome resultados decentes.
Además de reducir aún más , ¿alguien puede sugerir cómo optimizar el código anterior para que se ejecute más rápido? ¿O incluso puede sugerir un método de integración más rápido?
c++
performance
usuario2970116
fuente
fuente
trapezoidal_integration
lugar detrap
,sum
o enrunning_total
lugar des
(y también usar en+=
lugar des = s +
),trapezoid_width
o endx
lugar deh
(o no, dependiendo de su notación preferida para la regla trapezoidal), y cambiarfunc1
yfunc2
reflejar el hecho de que son valores, no funciones. Por ejemplo,func1
->previous_value
yfunc2
->current_value
, o algo así.Respuestas:
Matemáticamente, su expresión es equivalente a:
Entonces podrías implementar eso. Como se dijo, el tiempo probablemente esté dominado por la evaluación de la función, por lo que para obtener la misma precisión, puede usar un mejor método de integración que requiera menos evaluaciones de la función.
La cuadratura gaussiana es, en la actualidad, poco más que un juguete; solo es útil si requiere muy pocas evaluaciones. Si quieres algo fácil de implementar, puedes usar la regla de Simpson, pero no iría más allá del orden sin una buena razón.1/N3
Si la curvatura de la función cambia mucho, podría usar una rutina de pasos adaptativos, que seleccionaría un paso más grande cuando la función es plana y uno más pequeño y más preciso cuando la curvatura es más alta.
fuente
Lo más probable es que la evaluación de las funciones sea la parte más lenta de este cálculo. Si ese es el caso, entonces debería enfocarse en mejorar la velocidad de func () en lugar de tratar de acelerar la rutina de integración en sí.
Dependiendo de las propiedades de func (), también es probable que pueda obtener una evaluación más precisa de la integral con menos evaluaciones de funciones utilizando una fórmula de integración más sofisticada.
fuente
¿Posible? Si. ¿Útil? No. Es poco probable que las optimizaciones que voy a enumerar aquí hagan más que una pequeña fracción de un porcentaje de diferencia en el tiempo de ejecución. Un buen compilador ya puede hacer esto por usted.
De todos modos, mirando tu circuito interno:
En cada iteración de bucle, realiza tres operaciones matemáticas que pueden llevarse al exterior: sumar
j + h
, multiplicar por0.5
y multiplicar porh
. Lo primero que puede solucionar iniciando su variable iterador ena + h
, y los otros factorizando las multiplicaciones:Aunque señalaría que al hacer esto, debido al error de redondeo de punto flotante, es posible perder la última iteración del bucle. (Esto también fue un problema en su implementación original). Para solucionarlo, use un
unsigned int
osize_t
contador:Como dice la respuesta de Brian, es mejor dedicar su tiempo a optimizar la evaluación de la función
func
. Si la precisión de este método es suficiente, dudo que encuentre algo más rápido para lo mismoN
. (Aunque podría ejecutar algunas pruebas para ver si, por ejemplo, Runge-Kutta le permite bajar loN
suficiente como para que la integración general lleve menos tiempo sin sacrificar la precisión).fuente
Hay varios cambios que recomendaría para mejorar el cálculo:
std::fma()
, que realiza una suma múltiple fusionada .h
, que podría acumular errores de redondeo.Además, haría varios cambios para mayor claridad:
a
yb
en la firma de la función.N
→n
,h
→dx
,j
→x2
,s
→accumulator
.n
a unint
.fuente
Si su función es un polinomio, posiblemente ponderado por alguna función (por ejemplo, una gaussiana), puede hacer una integración exacta en 3d directamente con una fórmula de cubicación (por ejemplo, http://people.sc.fsu.edu/~jburkardt/c_src/ stroud / stroud.html ) o con una cuadrícula escasa (por ejemplo, http://tasmanian.ornl.gov/ ). Estos métodos simplemente especifican un conjunto de puntos y pesos para multiplicar el valor de la función, por lo que son muy rápidos. Si su función es lo suficientemente suave como para ser aproximada por polinomios, entonces estos métodos aún pueden dar una muy buena respuesta. Las fórmulas están especializadas para el tipo de función que está integrando, por lo que puede llevar un poco de investigación encontrar la correcta.
fuente
Cuando intenta calcular una integral numéricamente, intenta obtener la precisión que desea con el menor esfuerzo posible, o, alternativamente, intenta obtener la mayor precisión posible con un esfuerzo fijo. Parece que se pregunta cómo hacer que el código de un algoritmo en particular se ejecute lo más rápido posible.
Eso puede darle un poco de ganancia, pero será poco. Existen métodos mucho más eficientes para la integración numérica. Google para "la regla de Simpson", "Runge-Kutta" y "Fehlberg". Todos funcionan de manera bastante similar al evaluar algunos valores de la función y agregar inteligentemente múltiplos de esos valores, produciendo errores mucho más pequeños con el mismo número de evaluaciones de funciones, o el mismo error con un número mucho más pequeño de evaluaciones.
fuente
Hay muchas maneras de hacer la integración, de las cuales la regla trapezoidal es la más simple.
Si sabes algo sobre la función real que estás integrando, puedes hacerlo mejor si explotas eso. La idea es minimizar el número de puntos de cuadrícula dentro de niveles aceptables de error.
Por ejemplo, trapezoidal está haciendo un ajuste lineal a puntos consecutivos. Podría hacer un ajuste cuadrático, que si la curva es suave, se ajustaría mejor, lo que podría permitirle usar una cuadrícula más gruesa.
Las simulaciones orbitales a veces se realizan utilizando cónicas, porque las órbitas se parecen mucho a las secciones cónicas.
En mi trabajo, estamos integrando formas que se aproximan a las curvas en forma de campana, por lo que es efectivo modelarlas de esa manera ( la cuadratura gaussiana adaptativa se considera el "estándar de oro" en este trabajo).
fuente
Entonces, como se ha señalado en otras respuestas, esto depende en gran medida de lo costosa que sea su función. La optimización de su código trapz solo vale la pena si realmente es su cuello de botella. Si no es completamente obvio, debe verificar esto perfilando su código (herramientas como Intels V-tune, Valgrind o Visual Studio pueden hacer esto).
Sin embargo, sugeriría un enfoque completamente diferente: la integración de Monte Carlo . Aquí simplemente aproxima la integral muestreando su función en puntos aleatorios agregando los resultados. Vea este pdf además de la página wiki para más detalles.
Esto funciona extremadamente bien para datos de alta dimensión, típicamente mucho mejor que los métodos de cuadratura utilizados en la integración 1-d.
El caso simple es muy fácil de implementar (vea el pdf), solo tenga cuidado de que la función aleatoria estándar en c ++ 98 sea bastante mala tanto en rendimiento como en calidad. En c ++ 11, puede usar el Mersenne Twister en.
Si su función tiene mucha variación en algunas áreas y menos en otras, considere el uso de muestreo estratificado. Sin embargo, recomendaría usar la biblioteca científica GNU , en lugar de escribir la suya propia.
fuente
"recursivamente" es la clave. Está pasando por un gran conjunto de datos y está considerando una gran cantidad de datos más de una vez, o en realidad está generando su conjunto de datos usted mismo a partir de funciones (por partes).
Las integraciones evaluadas recursivamente serán ridículamente caras y ridículamente imprecisas a medida que aumentan los poderes en la recursividad.
Cree un modelo para interpolar su conjunto de datos y realice una integración simbólica por partes. Dado que muchos datos se están colapsando en coeficientes de funciones básicas, la complejidad para una recursión más profunda crece polinomialmente (y generalmente con potencias más bien bajas) en lugar de exponencialmente. Y obtienes resultados "exactos" (aún debes encontrar buenos esquemas de evaluación para obtener un rendimiento numérico razonable, pero aún así debería ser bastante factible mejorar la integración trapezoidal).
Si observa las estimaciones de error para las reglas trapezoidales, encontrará que están relacionadas con alguna derivada de las funciones involucradas, y si la integración / definición se realiza de forma recursiva, las funciones no tenderán a tener derivadas con buen comportamiento .
Si su única herramienta es un martillo, cada problema parece un clavo. Si bien apenas toca el problema en su descripción, tengo la sospecha de que aplicar la regla trapezoidal de forma recursiva es una mala coincidencia: obtiene una explosión de inexactitud y requisitos computacionales.
fuente
fuente