Mientras intentaba implementar mi versión del método de Euler para simular una EDE en C++, me encontré con un problema. Ocurre en algunos casos que la trayectoria generada por mi método acaba dando valores que son NaN
(no es un número). Esto ocurre en raras ocasiones, pero normalmente aparecen alrededor de 1 o 2 de estos casos si calculo 100 trayectorias.
Creo que el problema surge con los números aleatorios normales $Z_i$ que estoy usando para la iteración $$S_{i+1}=S_i+f(S_i)\cdot \sqrt{dt}\cdot Z_i$$ (donde el SDE es por ejemplo $dS_t=f(S_t)dW_t$ ). He probado a utilizar ambos std::normal_distribution
con generador std::mt199737
e implementando mi propio método Box-Muller (generando números uniformes con std::uniform_real_distribution
).
Con ambos enfoques, siempre hay un valor extraño que hace que toda la simulación vaya al infinito y produzca NaN
. El caso es que necesito hacer algunos cálculos con los valores finales $S_1$ producido por mis caminos.
¿Debo ignorar el NaN
¿? El número de ellos es muy reducido en comparación con el número total de caminos.
¿Hay gente que tenga este tipo de problemas al realizar este tipo de simulación?
Aquí está mi código:
#include <iostream>
#include <vector>
#include <random>
typedef double ( *funct )( double );
double square( double x ) { return x * x; }
double fourx( double x ) { return 4 * x; }
double generateGaussian();
std::vector< double > createPath( unsigned, double, funct );
int main()
{
double initial = 1.0;
unsigned intervals = 3000;
unsigned paths = 500;
std::vector< double > path;
int n = 0;
for ( auto j = 0; j < paths; ++j )
{
path = createPath( intervals, initial, square );
if ( isnan( path.back() ) )
++n;
}
std::cout << n << std::endl;
}
double generateGaussian()
{
static std::mt19937 generator( std::random_device{}() );
std::normal_distribution< double > dist( 0.0, 1.0 );
return dist( generator );
}
std::vector< double > createPath( unsigned numberOfIntervals, double initialCondition, funct f )
{
double sqdt = sqrt( 1.0 / numberOfIntervals );
std::vector< double > path;
path.push_back( initialCondition );
for ( auto i = 0; i < numberOfIntervals; ++i )
{
double Z = generateGaussian();
double Si = path.back();
double S = Si + f( Si ) * sqdt * Z;
path.push_back( S );
}
return path;
}
Simplemente lo compilo con
g++ -std=c++11 test.cpp
Esto es en un terminal en Mac OS X 10.10.5:
g++ -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
De vez en cuando me sale una ruta que termina con un NaN
. También implementé esto con otras funciones y obtuve ocurrencias similares. Probé, por ejemplo, tomando $f(x)=4x$ (así $dS_t=4S_tdW_t$ ) y a veces obtengo números inusualmente grandes (incluso ignorando el NaN
caminos). También en este caso particular de $f(x)=4x$ En cuanto a los resultados, los resultados son totalmente incoherentes de una carrera a otra, lo que me molesta, pero aún no he pensado en el motivo.
Finalmente, si inicializo el generador con 0, obtengo, con este mismo código, 2 NaN
's.