Tôi đã viết một mã trước đây để tính mà không sử dụng các hàm thư viện. Hôm qua, tôi đã xem lại mã cũ và tôi đã cố gắng làm cho nó nhanh nhất có thể, (và chính xác). Đây là nỗ lực của tôi cho đến nay:
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;
}
Ở đây tôi đang cố gắng tìm cái sao cho chỉ hơn n, và sau đó tôi thêm giá trị logarit của \ frac {n} {e ^ {a}} , nhỏ hơn 1. Tại thời điểm này, việc mở rộng Taylor của nhật ký (1 \ - \ x) có thể được sử dụng mà không phải lo lắng.n log(1-x)
Gần đây tôi đã có hứng thú với phân tích số và đó là lý do tại sao tôi không thể giúp đặt câu hỏi, đoạn mã này có thể chạy nhanh hơn bao nhiêu trong thực tế, trong khi đủ chính xác? Tôi có cần chuyển sang một số phương pháp khác không, ví dụ, sử dụng phân số tiếp tục, như thế này ?
Hàm được cung cấp với thư viện chuẩn C nhanh hơn gần 5,1 lần so với triển khai này.
CẬP NHẬT 1 : Sử dụng chuỗi arctan hyperbol được đề cập trong Wikipedia , tính toán dường như chậm hơn gần 2,2 lần so với chức năng nhật ký thư viện chuẩn C. Mặc dù, tôi đã không kiểm tra rộng rãi hiệu suất và đối với số lượng lớn hơn, việc triển khai hiện tại của tôi dường như thực sự chậm. Tôi muốn kiểm tra cả hai triển khai của mình để biết lỗi bị ràng buộc và thời gian trung bình cho một phạm vi số rộng nếu tôi có thể quản lý. Đây là nỗ lực thứ hai của tôi.
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;
}
Bất kỳ đề nghị hoặc phê bình đều được đánh giá cao.
CẬP NHẬT 2: Dựa trên các đề xuất được đưa ra dưới đây, tôi đã thêm một số thay đổi gia tăng ở đây, chậm hơn khoảng 2,5 lần so với triển khai thư viện tiêu chuẩn. Tuy nhiên, tôi đã thử nghiệm nó chỉ cho các số nguyên lần này, với số lượng lớn hơn thời gian chạy sẽ tăng lên. Để bây giờ. Tôi chưa biết các kỹ thuật để tạo số kép ngẫu nhiên , vì vậy nó chưa được điểm chuẩn đầy đủ. Để làm cho mã mạnh mẽ hơn, tôi đã thêm các sửa chữa cho các trường hợp góc. Lỗi trung bình cho các bài kiểm tra tôi thực hiện là khoảng . ≤ 1 e 308 4 đ - 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;
}