Había escrito un código hace un tiempo que intentaba calcular sin usar las funciones de la biblioteca. Ayer, estaba revisando el código anterior, e intenté hacerlo lo más rápido posible (y correcto). Aquí está mi intento hasta ahora:
const double ee = exp(1);
double series_ln_taylor(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now;
int i, flag = 1;
if ( n <= 0 ) return 1e-300;
if ( n * ee < 1 )
n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
for ( term = 1; term < n ; term *= ee, lgVal++ );
n /= term;
/* log(1 - x) = -x - x**2/2 - x**3/3... */
n = 1 - n;
now = term = n;
for ( i = 1 ; ; ){
lgVal -= now;
term *= n;
now = term / ++i;
if ( now < 1e-17 ) break;
}
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}
Aquí estoy tratando de encontrar para que esté justo por encima de n, y luego agrego el valor del logaritmo de , que es menor que 1. En este punto, La expansión Taylor de se puede utilizar sin preocuparse.e a n log(1-x)
Recientemente he crecido el interés en el análisis numérico, y es por eso que no puedo evitar hacer la pregunta, ¿cuánto más rápido se puede ejecutar este segmento de código en la práctica, sin dejar de ser lo suficientemente correcto? ¿Necesito cambiar a otros métodos, por ejemplo, usando una fracción continua, como esta ?
La función proporcionada con la biblioteca estándar de C es casi 5.1 veces más rápida que esta implementación.
ACTUALIZACIÓN 1 : Usando la serie de arctanos hiperbólicos mencionados en Wikipedia , el cálculo parece ser casi 2.2 veces más lento que la función de registro de biblioteca estándar de C. Sin embargo, no he comprobado exhaustivamente el rendimiento, y para grandes cantidades, mi implementación actual parece REALMENTE lenta. Si puedo administrar, quiero verificar tanto la implementación de errores como el tiempo promedio para una amplia gama de números. Aquí está mi segundo esfuerzo.
double series_ln_arctanh(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now, sm;
int i, flag = 1;
if ( n <= 0 ) return 1e-300;
if ( n * ee < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
for ( term = 1; term < n ; term *= ee, lgVal++ );
n /= term;
/* log(x) = 2 arctanh((x-1)/(x+1)) */
n = (1 - n)/(n + 1);
now = term = n;
n *= n;
sm = 0;
for ( i = 3 ; ; i += 2 ){
sm += now;
term *= n;
now = term / i;
if ( now < 1e-17 ) break;
}
lgVal -= 2*sm;
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}
Cualquier sugerencia o crítica es apreciada.
ACTUALIZACIÓN 2: según las sugerencias que se hacen a continuación, he agregado algunos cambios incrementales aquí, que es aproximadamente 2.5 veces más lento que la implementación estándar de la biblioteca. Sin embargo, esta vez lo he probado solo para enteros , para números más grandes el tiempo de ejecución aumentaría. Por ahora. Todavía no conozco técnicas para generar números dobles aleatorios , por lo que aún no está completamente comparado. Para hacer que el código sea más robusto, he agregado correcciones para los casos de esquina. El error promedio para las pruebas que hice es de alrededor de . ≤ 1 e 308 4 e - 15
double series_ln_better(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now, sm;
int i, flag = 1;
if ( n == 0 ) return -1./0.; /* -inf */
if ( n < 0 ) return 0./0.; /* NaN*/
if ( n < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
/* the cutoff iteration is 650, as over e**650, term multiplication would
overflow. For larger numbers, the loop dominates the arctanh approximation
loop (with having 13-15 iterations on average for tested numbers so far */
for ( term = 1; term < n && lgVal < 650 ; term *= ee, lgVal++ );
if ( lgVal == 650 ){
n /= term;
for ( term = 1 ; term < n ; term *= ee, lgVal++ );
}
n /= term;
/* log(x) = 2 arctanh((x-1)/(x+1)) */
n = (1 - n)/(n + 1);
now = term = n;
n *= n;
sm = 0;
/* limiting the iteration for worst case scenario, maximum 24 iteration */
for ( i = 3 ; i < 50 ; i += 2 ){
sm += now;
term *= n;
now = term / i;
if ( now < 1e-17 ) break;
}
lgVal -= 2*sm;
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}