Bir süre önce kütüphane fonksiyonlarını kullanmadan hesaplamaya çalışan bir kod yazmıştım . Dün eski kodu gözden geçiriyordum ve mümkün olduğunca çabuk yapmaya çalıştım (ve doğru). İşte şimdiye kadar denemem:
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;
}
Burada e bulmaya çalışıyorum ki e a sadece n'nin üzerinde ve sonra n'nin logaritma değerini ekliyorum , ki bu 1'den küçüktür. Bu noktada,log(1-x) 'in Taylor genişlemesiendişelenmeden kullanılabilir.
Son zamanlarda sayısal analiz bir ilgi büyüdü ve bu yüzden soru doğru sormaya yardımcı olamaz, yeterince doğru olurken bu kod segmenti pratikte ne kadar hızlı çalıştırılabilir? Ben gibi, sürekli kısmını kullanarak, örneğin, bazı diğer yöntemlere geçmek gerekiyor mu bu ?
C standart kütüphane ile temin edilmekte olan işlev, neredeyse 5.1 kat daha hızlı, bu uygulama daha fazladır.
GÜNCELLEME 1 : Wikipedia'da belirtilen hiperbolik arktan serisini kullanarak, hesaplama C standart kütüphane log fonksiyonundan neredeyse 2.2 kat daha yavaş görünüyor. Gerçi, performansı kapsamlı bir şekilde kontrol etmedim ve daha büyük sayılar için mevcut uygulamam GERÇEKTEN yavaş görünüyor. Uygulamamın her ikisini de hataya bağlı olup olmadığını ve çok sayıda sayı için ortalama süreyi kontrol edebilirsem kontrol etmek istiyorum. İşte ikinci çabam.
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;
}
Herhangi bir öneri veya eleştiri takdir edilmektedir.
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;
}