2016-07-22 6 views
0

背景:ガウス関数を無限級数の和として計算する次のコードがあります。 Gaussian Functionは、最も簡単な形式ではe^- (x^2)です。 これは、テイラー級数展開を使用して、無限級数の和として計算できます。Math.expの結果と矛盾する無限級数の和

したがって、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

ここでは何が欠けていますか?

+0

私は浮動小数点精度を推測するでしょうか?これを防ぐためにBigDecimalを試すことができます(理由がある場合) – AxelH

+1

なぜ私はfactorialの代わりに 'i'で割りますか? – jr593

+0

@AxelH:私は最大の値についていくつかの計算をしました。 x = 1およびn = 100の場合、100!が計算され、9.332622e + 157となる。 x = 5およびn = 100の場合、5×200が計算され、6.223015e + 139である。それが範囲の問題だったなら、私はそれがx = 1&n = 100で墜落していたはずだと思います。 – Haxiel

答えて

2

あなたのアルゴリズムはうまく見えますし、おそらく倍精度の限界にぶつかっています。

(x)の代わりに、EXPコードをもう少し簡単です(-x2)、の私はEXPのテイラー級数のためのアルゴをリライトすることをお勧め:

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の使用してそのメソッドを書き換えることができます。

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(); 
} 

を、それはあなたが期待するものに近い結果を返す必要があります。

+0

より簡単なexp(x)アルゴリズムを使用していただきありがとうございます。しかし、 'BigDecimal'を使ってメソッドを書き直すと、x = 5、n = 100(exp(-x^2)の場合)になります。その後、前と同じように矛盾します。 BigDecimalの精度限界に達したのですか? – Haxiel

+0

@XSurgentは反復回数を増やそうとします。xの値が小さいほど、シリーズの収束に時間がかかります。 n = 200の場合、x = 6の 'Math.exp'に非常に近いものが得られます。x = 7の場合は、例えば' new MathContext(50、RoundingMode.HALF_EVEN) 'で精度を上げる必要があります。 – assylias

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のは精度を失うが、私は本当にわからないと言うでしょう;)

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(プラスまたはマイナス3標準偏差)の範囲に入ることがわかります。あなたはその範囲外のパラメータを必要としそうもありません。

関連する問題