2016-07-22 2 views
0

Hintergrund: Ich habe den folgenden Code, der die Gaußfunktion als eine Summe einer unendlichen Reihe berechnet. Die Gaussian Function, in seiner einfachsten Form ist e^- (x^2). Dieser Wert kann als Summe einer unendlichen Reihe unter Verwendung der Taylor-Reihen-Erweiterung berechnet werden.Summe der unendlichen Reihen, die nicht mit Math.exp übereinstimmen

Daher ist 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; 
    } 

Als Vergleich verwende ich Math.exp(-(x*x)) zu sehen, ob meine Funktion ordnungsgemäß funktioniert.

Die Funktion scheint für niedrige Werte von x zu funktionieren, verhält sich aber danach inkonsistent. Hier die Ausgabe einiger Testfälle:

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

Was fehlt mir hier?

+0

Ich würde schätzen Floating-Nummer Präzision? Du könntest es mit BigDecimal versuchen, um dies zu verhindern (wenn das der Grund ist) – AxelH

+1

Warum dividierst du durch "i" statt "i faktoriell"? – jr593

+0

@AxelH: Ich habe ein paar Berechnungen zu den größten Werten gemacht. Wenn x = 1 & n = 100, 100! berechnet, was 9,332622e + 157 ergibt. Wenn x = 5 & n = 100, wird 5^200 berechnet, was 6,223015e + 139 ist. Wenn es sich um ein Bereichsproblem handelte, sollte es mit x = 1 & n = 100 abgestürzt sein. – Haxiel

Antwort

2

Ihr Algorithmus sieht gut aus und Sie stoßen wahrscheinlich an die Grenzen der doppelten Genauigkeit.

Ich würde vorschlagen, die algo für die Taylor-Reihe von exp (x) anstelle von exp (x2) neu zu schreiben, die ein wenig einfacher zu Code ist:

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

Sie können dann eine expSeries_X2(x, i) { return expSeries(-x*x, i); } hinzufügen wenn du willst.

Wir können dann umschreiben mit dieser Methode 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(); 
} 

Und es sollte ein Ergebnis zurück, das näher an dem, was Sie erwarten.

+0

Danke, dass Sie darauf hingewiesen haben, den einfacheren Algorithmus von exp (x) zu verwenden. Das Umschreiben der Methode mit 'BigDecimal' bringt mich jedoch auf x = 5, n = 100 (für exp (-x^2)). Danach wird es wie zuvor inkonsistent. Triff ich jetzt die Präzisionsgrenzen für 'BigDecimal'? – Haxiel

+0

@XSurgent versuchen, die Anzahl der Iterationen zu erhöhen - die Konvergenz der Serie dauert länger für kleinere Werte von x. Mit n = 200 bekomme ich etwas sehr nahe an 'Math.exp' für x = 6. Für x = 7 müssen Sie auch die Genauigkeit erhöhen, zum Beispiel mit' new MathContext (50, RoundingMode.HALF_EVEN)) '. – assylias

0

Ich habe die Formel mit BigDecimal rewriten:

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

Und das Ergebnis:

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 

Ich würde sagen, dass Math.exp Präzision verliert, aber ich bin nicht wirklich sicher;)

2

Dies ist eine großartige Lektion in Problemen mit Gleitkommazahlen.

Taylor-Serie ist nicht immer ein guter Weg, um einen Funktionswert zu berechnen.

Schauen Sie sich die allgemeine Definition here an. Sie berechnen den Wert der Funktion durch extrapolieren heraus von einem bestimmten Punkt a. In Ihrem Fall ist dieser Wert gleich Null, also exp(0) = 1. Je weiter Sie von Null entfernt sind, desto schlechter ist die Extrapolation. So ist es bei allen Extrapolationen, egal wie du es machst.

Noch schlimmer, Sie sind abhängig von wechselnden Zeichen von sehr große Zahlen, um sich gegenseitig aus und geben Ihnen etwas Sinnvolles. Wenn x = 7 und e = 2.71...., wie groß ist eine Zahl 2^49 oder 3^49? Sehr groß, in der Tat.

Ich glaube nicht, dass die Antwort BigDecimal sein sollte.Eine bessere Idee wäre, genau zu verstehen, was Sie tun, und herauszufinden, ob es bessere Möglichkeiten gibt, die Funktion für große Exponenten zu approximieren.

Gaussian wird in der Statistik verwendet, um die Normalverteilung zu modellieren. Wenn Sie den Funktionsparameter auf Z-score (Z = (x-xmean)/stddev) normalisieren, werden Sie sehen, dass 99,9% der Fläche unter der Funktion in den Bereich -3 <= Z <= +3 (plus oder minus drei Standardabweichungen) fällt. Sie brauchen wahrscheinlich keinen Parameter, der außerhalb dieses Bereichs liegt.

Verwandte Themen