无限级数的和与Math.exp结果不一致

时间:2016-07-22 09:49:32

标签: java math

背景:我有以下代码将高斯函数计算为无穷级数的和。 Gaussian Function,其最简单的形式是e ^ - (x ^ 2)。 这可以使用泰勒级数展开计算为无穷级数的总和。

因此,e ^ - (x ^ 2)= 1 - (x ^ 2)+(x ^ 4)/ 2! - (x ^ 6)/ 3! ....

public static double expSeries (double x, int n){
    double result = 0.0, x0 = 1.0, x1;
    result+= x0;
    for (int i=1; i<=n; i++){
        x1 = x0 * ((x*x)/i);
        if (i%2 == 0){
            result += x1;
        } else {
            result -= x1;
        }           
        x0 = x1;
    }
    return result;
    }

作为比较,我使用Math.exp(-(x*x))来查看我的功能是否正常工作。

该函数似乎适用于x的低值,但在此之后表现不一致。以下是一些测试用例的输出:

x=1; n=10
Result  : 0.3678794642857144
Math.exp: 0.36787944117144233

x=1; n=100
Result  : 0.36787944117144245
Math.exp: 0.36787944117144233

x=2; n=100
Result  : 0.018315638888733953
Math.exp: 0.01831563888873418

x=3; n=100
Result  : 1.234098038990534E-4
Math.exp: 1.2340980408667956E-4

x=4; n=100
Result  : 1.1247503313371918E-7
Math.exp: 1.1253517471925912E-7

x=5; n=100
Result  : 8.181278981021932E-7
Math.exp: 1.3887943864964021E-11

x=6; n=100
Result  : -0.03197975209642004
Math.exp: 2.319522830243569E-16

x=7; n=100
Result  : 3.6698962220692825E10
Math.exp: 5.242885663363464E-22

我在这里缺少什么?

3 个答案:

答案 0 :(得分:2)

你的算法看起来很好,你可能达到了双精度的极限。

我建议改写exp(x)的泰勒系列而不是exp(-x2)的算法,这对代码来说更直接:

public static double expSeries(double x, int n) {
  double term = 1;
  double result = term;
  for (int i = 1; i <= n; i++) {
    term *= x / i;
    result += term;
  }
  return result;
}

如果需要,您可以添加expSeries_X2(x, i) { return expSeries(-x*x, i); }

然后我们可以使用BigDecimal s:

重写该方法
public static double expSeries(double x, int n) {
  BigDecimal result = ONE;
  BigDecimal term = ONE;
  BigDecimal x_ = new BigDecimal(x);
  for (int i = 1; i <= n; i++) {
    term = term.multiply(x_.divide(BigDecimal.valueOf(i), MathContext.DECIMAL128));
    result = result.add(term);
  }
  return result.doubleValue();
}

它应该返回一个更接近你期望的结果。

答案 1 :(得分:2)

这是浮点数问题的一个很好的教训。

泰勒系列总是计算函数值的好方法。

查看一般定义here。您通过从特定点a外推来计算函数的值。在您的情况下,该值为零,因此exp(0) = 1。从零开始越远,外推越差。所以无论你怎么做,所有推断都是如此。

更糟糕的是,你依赖于非常大号的交替标志来互相取消并给你一些明智的东西。如果x = 7e = 2.71....,数字的大小是2 ^ 49还是3 ^ 49?确实很大。

我认为答案不应该是BigDecimal。一个更好的想法是准确理解你正在做什么,并找出是否有更好的方法来近似大指数的函数。

高斯在统计学中用于模拟正态分布。如果您将函数参数规范化为Z-scoreZ = (x-xmean)/stddev),您会看到函数下99.9%的区域属于-3 <= Z <= +3范围(正负三个标准)偏差)。您不可能需要超出该范围的参数。

答案 2 :(得分:0)

我已使用BigDecimal重写公式:

public static void main(String... args){
    for(int i=1;i < 8; ++i){
        double l = Math.exp(-(Math.pow(i, 2)));
        double r = expSeries(BigDecimal.valueOf(i), 100);
        System.out.println( l + " - " + r + " = " + (l - r) );
    }     
}

public static double expSeries (BigDecimal x, int n){
    BigDecimal result = BigDecimal.ONE, x1;
    for (int i=1; i<=n; i++){
        x1 = x.pow(i*2).divide(new BigDecimal(factorial(BigInteger.valueOf(i))), MathContext.DECIMAL128);
        if (i%2 == 0) {
            result = result.add(x1);
        }
        else{
            result = result.subtract(x1);
        }
    }
    return result.doubleValue();
}

public static BigInteger factorial (BigInteger num){
    if (num.compareTo(BigInteger.ONE) == 0) return num;
    return num.multiply(
            factorial(num.subtract(BigInteger.ONE)));
}

结果:

0.36787944117144233 - 0.36787944117144233 = 0.0
0.01831563888873418 - 0.01831563888873418 = 0.0
1.2340980408667956E-4 - 1.2340980408667956E-4 = 0.0
1.1253517471925912E-7 - 1.1253517471925912E-7 = 0.0
1.3887943864964021E-11 - 1.3887943997473953E-11 = -1.3250993165605518E-19
2.3195228302435696E-16 - 0.0012040908282411062 = -0.0012040908282408742
5.242885663363464E-22 - 3.6698962251221756E10 = -3.6698962251221756E10

我会说Math.exp会失去精确度,但我并不确定;)