Taylor系列exp(-x)和exp(+ x) [英] Taylor Series Difference between exp(-x) and exp(+x)

查看:2433
本文介绍了Taylor系列exp(-x)和exp(+ x)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写一个程序,计算exp(-x)和exp(x)的泰勒级数高达200次迭代,对于大x。 (exp(x)= 1 + x + x ^ 2/2 + ...)。



我的程序非常简单, 。然而,它对于exp(-x)发散,但是对于exp(+ x)​​恰好收敛。这里是我的代码到目前为止:

  long double x = 100.0,sum = 1.0,last = 

for(int i = 1; i <200; i ++){
last * = x / i; //将前一项中的最后一个项乘以x / n
sum + = last; // add this new last term to the sum
}
cout<< exp(+ x)​​=<< sum< endl;

x = -100.0; // redo but now letting x< 0
sum = 1.0;
last = 1.0;

for(int i = 1; i <200; i ++){
last * = x / i;
sum + = last;
}
cout<< exp(-x)=<< sum< endl;

当我运行它,我得到以下输出:

  exp(+ x)​​= 2.68811691354e + 43 
exp(-x)= -8.42078025179e + 24



当实际值为:

  exp (+ x)​​= 2.68811714182e + 43 
exp(-x)= 3.72007597602e-44

正如你所看到的,它对正值计算很好,但不是负值。有谁有任何想法为什么舍入错误可能会这样错误,只是添加一个负面的每一个其他术语?



提前感谢!

解决方案

我不认为这与浮点近似误差有关,我认为还有另一个更重要的错误来源。



正如你自己说的,你的方法真的很简单。您正在对 x = 0 采用泰勒级数近似,然后在 x = -100



你实际上希望这种方法有多准确?为什么?



应该只期望您的方法在 x = 0 附近的狭窄区域中是准确的。泰勒的近似定理告诉你,如果您取 x = 0 系列的 N 条款,您的近似值将精确到 O(| x |)^(N + 1)。所以,如果你采取200条款,你应该准确的例如。 [ - 0.5,0.5] 范围内 10 ^( - 60) x = 100 泰勒定理只会给你一个非常可怕的约束。



从概念上讲, c $ c> e ^ { - x} 趋于零,因为 x 变为负无穷大。但是你的近似函数是一个固定程度的多项式,任何非常数多项式倾向于无穷大或无限渐近加无穷大。因此,如果您考虑 x 的所有可能值的范围,相对误差必须是无限的。



我想你应该重新思考你的做法。你可以考虑的一个事情是,只使用泰勒级数法满足 x 的值满足 -0.5f <= x <= 0.5f 。对于 x 大于 0.5f ,您尝试将 x 两个并递归调用该函数,然后对结果求平方。



为了获得最佳效果,您应该使用已建立的方法。



p>

我决定写一些代码,看看我的想法是如何工作。它似乎完全符合范围 x = -10000 x = 10000 的C库实现至少到与我显示的精度一样多。 :)



请注意,即使对于 x 大于100的值,我的方法也是准确的,方法实际上在正端也失去准确性。

  #include< cmath> 
#include< iostream>

long double taylor_series(long double x)
{
long double sum = 1.0,last = 1.0;

for(int i = 1; i <200; i ++){
last * = x / i; //将前一项中的最后一个项乘以x / n
sum + = last; // add this new last term to the sum
}

return sum;
}

long double hybrid(long double x)
{
long double temp;
if(-0.5< = x&& x< = 0.5){
return taylor_series(x);
} else {
temp = hybrid(x / 2);
return(temp * temp);
}
}

long double true_value(long double x){
return expl(x);
}

void output_samples(long double x){
std :: cout< x =<< x<< std :: endl;
std :: cout<< \ttaylor series =< taylor_series(x)<< std :: endl;
std :: cout<< \ thybrid method =<混合(x) std :: endl;
std :: cout<< \tlibrary =<< true_value(x)<< std :: endl;
}

int main(){
output_samples(-10000);
output_samples(-1000);
output_samples(-100);
output_samples(-10);
output_samples(-1);
output_samples(-0.1);
output_samples(0);
output_samples(0.1);
output_samples(1);
output_samples(10);
output_samples(100);
output_samples(1000);
output_samples(10000);
}

输出:

  $ ./main 
x = -10000
taylor series = -2.48647e + 423
hybrid method = 1.13548e-4343
library = 1.13548e-4343
x = -1000
taylor series = -2.11476e + 224
hybrid method = 5.07596e-435
library = 5.07596e-435
x = 100
taylor series = -8.49406e + 24
hybrid method = 3.72008e-44
library = 3.72008e-44
x = -10
taylor series = 4.53999e -05
hybrid method = 4.53999e-05
library = 4.53999e-05
x = -1
taylor series = 0.367879
hybrid method = 0.367879
库= 0.367879
X = -0.1
泰勒级数= 0.904837
混合方法= 0.904837
库= 0.904837
X = 0
泰勒级数= 1
hybrid method = 1
library = 1
x = 0.1
taylor series = 1.10517
hybrid method = 1.10517
library = 1.10517
x = 1
泰勒级数= 2.71828
混合方法= 2.71828
库= 2.71828
X = 10
泰勒级数= 22026.5
混合方法= 22026.5
库= 22026.5
x = 100
taylor series = 2.68812e + 43
混合法= 2.68812e + 43
库= 2.68812e + 43
x = 1000
泰勒系列= 3.16501e + 224
混合方法= 1.97007e + 434
库= 1.97007e + 434
X = 10000
泰勒级数= 2.58744e + 423
混合方法= 8.80682 e + 4342
library = 8.80682e + 4342

编辑:



有关谁是有兴趣:



有在对浮点错误是如何显著是在原计划的意见中提出的一些问题。我原来的假设是他们是微不足道的 - 我做了一个测试,看看是否是真的。事实证明,这不是真的,且有显著浮点错误怎么回事,但即使没有浮点错误,也有仅通过泰勒级数引入显著的错误。泰勒系列在 x = -100 时的真实价值似乎接近 -10 ^ {24} ,而不是 10 ^ { - 44} 。我检查了这一点使用的boost ::多倍:: cpp_rational ,这是建立在他们的任意精度的整数类型的任意精度理智型。



输出:

  x = -100 
taylor series(double)= -8.49406e + 24
(理性)= -18893676108550916857809762858135399218622904499152741157985438973568808515840901824148153378967545615159911801257288730703818783811465589393308637433853828075746484162303774416145637877964256819225743503057927703756503421797985867950089388433370741907279634245166982027749118060939789786116368342096247737/2232616279628214542925453719111453368125414939204152540389632950466163724817295723266374721466940218188641069650613086131881282494641669993119717482562506576264729344137595063634080983904636687834775755173984034571100264999493261311453647876869630211032375288916556801211263293563
= -8.46257e + 24
库= 3.72008e-44
X = 100
泰勒(双)= 2.68812e +43
(理性)= 36451035284924577938246208798747009164319474757880246359883694555113407009453436064573518999387789077985197279221655719227002367495061633272603038249747260895707250896595889294145309676586627989388740458641362406969609459453916777341749316070359589697827702813520519796940239276744754778199440304584107317957027129587503199/1356006206645357299077422810994072904566969809700681604285727988319939931024001696953196916719184549697395496290863162742676361760549235149195411231740418104602504325580502523311497039304043141691060121240640609954226541318710631103275528465092597490136227936213123455950399178299
= 2.68812e + 43
库= 2.68812e + 43

代码:

  #include< cmath> 
#include< iostream>
#include< boost / multiprecision / cpp_int.hpp>

typedef unsigned int uint;
typedef boost :: multiprecision :: cpp_rational rational;

// Taylor系列exp

模板< typename T>
T tlorlor_series(const T x){
T sum = 1,last = 1;

for(uint i = 1; i <200; i ++){
last = last *(x / i);
sum = sum + last
}
return sum;
}

void sample(const int x){
std :: cout< x =<< x<< std :: endl;
long double e1 = taylor_series(static_cast< long double>(x));
std :: cout<< \ttaylor series(double)=<< e1<< std :: endl;
rational e2 = taylor_series(static_cast< rational>(x));
std :: cout<< \t(rational)=< e2 < std :: endl;
std :: cout<< \t =<< static_cast< long double>(e2)< std :: endl;
std :: cout<< \tlibrary =<< expl(static_cast< long double>(x))<< std :: endl;
}

int main(){
sample(-100);
sample(100);
}


I'm trying to write a program which calculates the Taylor series of exp(-x) and exp(x) up to 200 iterations, for large x. (exp(x)=1+x+x^2/2+...).

My program is really simple, and it seems like it should work perfectly. However it diverges for exp(-x), but converges just fine for exp(+x). Here is my code so far:

long double x = 100.0, sum = 1.0, last = 1.0;

for(int i = 1; i < 200; i++) {
        last *= x / i;    //multiply the last term in the series from the previous term by x/n
        sum += last; //add this new last term to the sum
    }
cout << "exp(+x) = " << sum << endl;

x = -100.0; //redo but now letting x<0
sum = 1.0;
last = 1.0;

for(int i = 1; i < 200; i++) {
            last *= x / i;
            sum += last;
        }
    cout << "exp(-x) = " << sum << endl;

And when I run it, I get the following output:

exp(+x) = 2.68811691354e+43 
exp(-x) = -8.42078025179e+24

When the actual values are:

exp(+x) = 2.68811714182e+43 
exp(-x) = 3.72007597602e-44

As you can see, it works just fine for the positive calculation, but not negative. Does anyone have any ideas on why rounding errors could go so wrong by just adding a negative on every other term? Also, is there anything I could implement to fix this issue?

Thanks in advance!!

解决方案

I don't think this actually has anything to do with floating point approximation error, I think there is another more significant source of error.

As you say yourself, your method is really simple. You are taking a taylor series approximation at x=0 to this function, and then evaluating it at x=-100.

How accurate did you actually expect this method to be and why?

At a high level, you should only expect your method to be accurate in a narrow region near x=0. Taylor's approximation theorem tells you that e.g. if you take N terms of the series around x=0, your approximation will be accurate to O(|x|)^(N+1) at least. So if you take 200 terms, you should be accurate to e.g. within 10^(-60) or so in the range [-0.5, 0.5]. But at x=100 Taylor's theorem only gives you a pretty terrible bound.

Conceptually, you know that e^{-x} tends to zero as x goes to minus infinity. But your approximation function is a polynomial of a fixed degree, and any non-constant polynomial tends to either plus infinity or minus infinity asymptotically. So the relative error must be unbounded if you consider the whole range of possible values of x.

So in short I think you should rethink your approach. One thing you might consider is, only use the Taylor series method for x values satisfying -0.5f <= x <= 0.5f. And for any x greater than 0.5f, you try dividing x by two and calling the function recursively, then squaring the result. Or something like this.

For best results you should probably use an established method.

Edit:

I decided to write some code to see how well my idea actually works. It appears to match perfectly with the C library implementation across the range x = -10000 to x = 10000 at least to as many bits of precision as I am displaying. :)

Note also that my method is accurate even for values of x greater than 100, where the Taylor Series method actually loses accuracy on the positive end as well.

#include <cmath>
#include <iostream>

long double taylor_series(long double x)
{
    long double sum = 1.0, last = 1.0;

    for(int i = 1; i < 200; i++) {
            last *= x / i;    //multiply the last term in the series from the previous term by x/n
            sum += last; //add this new last term to the sum
    }

    return sum;
}

long double hybrid(long double x)
{
    long double temp;
    if (-0.5 <= x && x <= 0.5) {
        return taylor_series(x);
    } else {
        temp = hybrid(x / 2);
        return (temp * temp);
    }
}

long double true_value(long double x) {
    return expl(x);
}

void output_samples(long double x) {
    std::cout << "x = " << x << std::endl;
    std::cout << "\ttaylor series = " << taylor_series(x) << std::endl;
    std::cout << "\thybrid method = " << hybrid(x) << std::endl;
    std::cout << "\tlibrary = " << true_value(x) << std::endl;
}

int main() {
    output_samples(-10000);
    output_samples(-1000);
    output_samples(-100);
    output_samples(-10);
    output_samples(-1);
    output_samples(-0.1);
    output_samples(0);
    output_samples(0.1);
    output_samples(1);
    output_samples(10);
    output_samples(100);
    output_samples(1000);
    output_samples(10000);
}

Output:

$ ./main 
x = -10000
    taylor series = -2.48647e+423
    hybrid method = 1.13548e-4343
    library = 1.13548e-4343
x = -1000
    taylor series = -2.11476e+224
    hybrid method = 5.07596e-435
    library = 5.07596e-435
x = -100
    taylor series = -8.49406e+24
    hybrid method = 3.72008e-44
    library = 3.72008e-44
x = -10
    taylor series = 4.53999e-05
    hybrid method = 4.53999e-05
    library = 4.53999e-05
x = -1
    taylor series = 0.367879
    hybrid method = 0.367879
    library = 0.367879
x = -0.1
    taylor series = 0.904837
    hybrid method = 0.904837
    library = 0.904837
x = 0
    taylor series = 1
    hybrid method = 1
    library = 1
x = 0.1
    taylor series = 1.10517
    hybrid method = 1.10517
    library = 1.10517
x = 1
    taylor series = 2.71828
    hybrid method = 2.71828
    library = 2.71828
x = 10
    taylor series = 22026.5
    hybrid method = 22026.5
    library = 22026.5
x = 100
    taylor series = 2.68812e+43
    hybrid method = 2.68812e+43
    library = 2.68812e+43
x = 1000
    taylor series = 3.16501e+224
    hybrid method = 1.97007e+434
    library = 1.97007e+434
x = 10000
    taylor series = 2.58744e+423
    hybrid method = 8.80682e+4342
    library = 8.80682e+4342

Edit:

For who is interested:

There was some question raised in the comments about how significant the floating point errors are in the original program. My original assumption was that they were negligible -- I did a test to see if that's true or not. It turns out that's not true, and there is significant floating point error going on, but that even without the floating point errors there are significant errors being introduced just by the taylor series alone. The true value of the taylor series at 200 terms at x=-100 seems to be near to -10^{24}, not 10^{-44}. I checked this using boost::multiprecision::cpp_rational, which is an arbitrary precision rational type built on their arbitrary precision integer type.

Output:

x = -100
    taylor series (double) = -8.49406e+24
                (rational) = -18893676108550916857809762858135399218622904499152741157985438973568808515840901824148153378967545615159911801257288730703818783811465589393308637433853828075746484162303774416145637877964256819225743503057927703756503421797985867950089388433370741907279634245166982027749118060939789786116368342096247737/2232616279628214542925453719111453368125414939204152540389632950466163724817295723266374721466940218188641069650613086131881282494641669993119717482562506576264729344137595063634080983904636687834775755173984034571100264999493261311453647876869630211032375288916556801211263293563
                           = -8.46257e+24
    library                = 3.72008e-44
x = 100
    taylor series (double) = 2.68812e+43
                (rational) = 36451035284924577938246208798747009164319474757880246359883694555113407009453436064573518999387789077985197279221655719227002367495061633272603038249747260895707250896595889294145309676586627989388740458641362406969609459453916777341749316070359589697827702813520519796940239276744754778199440304584107317957027129587503199/1356006206645357299077422810994072904566969809700681604285727988319939931024001696953196916719184549697395496290863162742676361760549235149195411231740418104602504325580502523311497039304043141691060121240640609954226541318710631103275528465092597490136227936213123455950399178299
                           = 2.68812e+43
    library                = 2.68812e+43

Code:

#include <cmath>
#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>

typedef unsigned int uint;
typedef boost::multiprecision::cpp_rational rational;

// Taylor series of exp

template <typename T>
T taylor_series(const T x) {
    T sum = 1, last = 1;

    for (uint i = 1; i < 200; i++) {
        last = last * (x / i);
        sum = sum + last;
    }
    return sum;
}

void sample(const int x) {
    std::cout << "x = " << x << std::endl;
    long double e1 = taylor_series(static_cast<long double>(x));
    std::cout << "\ttaylor series (double) = " << e1 << std::endl;
    rational e2 = taylor_series(static_cast<rational>(x));
    std::cout << "\t            (rational) = " << e2 << std::endl;
    std::cout << "\t                       = " << static_cast<long double>(e2) << std::endl;
    std::cout << "\tlibrary                = " << expl(static_cast<long double>(x)) << std::endl;
}

int main() {
    sample(-100);
    sample(100);
}

这篇关于Taylor系列exp(-x)和exp(+ x)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆