Problem bei berechnen der eulerschen Zahl!



  • Hallo,
    ich versuche die eulersche Zahl auf 10 Stellen nach dem Komma genau zu berechnen,
    jedoch weiss ich einfach nicht, wie ich die Abbruchbedingung formulieren soll, damit die Berechnung bei 10 Stellen nach dem Komma stoppt.

    Ich habe mir überlegt die Summe jedesmal *10^10 zu rechnen, dann einer Int- und einer Double-Variablen zuordnen, wenn dann die Double-Variable - Int-Variable grösser als 0.00000001(nicht Null wegen Fliesskommazahlen) ist, dann sollte die Berechnung stoppen. Das Problem ist nur, dass der Int-Typ nicht 10^10 darstellen kann.

    Was ich bisher geschaft habe:

    [cpp]

    #include <iostream>
    #include <cmath>
    
    using namespace std;
    
    int main()
    {
        cout << "Die eulersche Zahl e lautet: \n";
    
        double k = 1;
        double sum = 1;
    
        cout.precision(11);    
    
        for (double i = 1 ;  ; i = i+1)
        {
            k = (1/i) *k;
            sum = (sum + k);
            }
    
        cout << sum << "\n";    
    
        cin.get();
        cin.get();
        return 0;
    }
    

    Ich hoffe Jemand kann mir einen Typ geben.



  • Dann nimm int64_t.



  • Dann meckert mir der Compiler ebenfalls:

    integer constant is too large for "long" type.


  • Mod

    Dann ist deine Standardbibliothek kaputt. (Da ich das nicht recht glauben kann, nehme ich mal an, dass du etwas falsch gemacht hast)

    Nichtsdestotrotz ist deine Abbruchbedingung so nicht richtig. Das prüft bloß, ob die 11. Stelle größer als 1 ist oder nicht. Was du tun kannst: Vergleiche deine Summenglieder. Wenn du die Restterme als <10^(-10) abschätzen kannst, dann bist du am Ziel.



  • tipp1: schätze den fehler für ein gegebenes i ab

    tipp2: weuu du zwei positive fließkommazahlen a und b hast, wobei a <= b*epsilon (also a viel kleiner als b ist) dann wird wahrscheinlich

    double c = a + b;
    if (b==c) {
      // dies hier zutreffen
    }
    

    weil b immernoch die nächste darstellbare double-Zahl ist (Rundungsfehler==a). Du könntest also auch aufhören, wenn sich "summe" nicht mehr verändert, was numerisch irgendwann der fall sein wird.

    ich kann mir aber auch vorstellen -- ohne jetzt danach gegoogelt zu haben -- dass es folgen oder reihen gibt, die noch schneller konvergieren. Diese Reihe, die Du da stehen hast, konvergiert glaub'ich nur superlinear. Da gibt's bestimmt auch noch welche, die mindestens quadratisch konvergieren und numerisch stabiler sind.



  • krümelkacker schrieb:

    dass es folgen oder reihen gibt, die noch schneller konvergieren. Diese Reihe, die Du da stehen hast, konvergiert glaub'ich nur superlinear. Da gibt's bestimmt auch noch welche, die mindestens quadratisch konvergieren und numerisch stabiler sind.

    Haste Dich verlesen?
    Er macht nicht
    sum(1/i) //langsam, ach die harmonische reihe konvergiert ja nichtmal, die alte sau
    sondern
    sum(1/fact(i)) //da würde ich eigentlich keine Bedenken anmelden



  • volkard schrieb:

    Haste Dich verlesen?
    Er macht [...]
    sum(1/fact(i))

    Ich habe mich nicht verlesen. Ich denke...
    sum(1/2i) // konvergiert linear
    sum(1/fact(i)) // konvergiert superlinear

    Aber du hast mich gerade etwas verunsichert.



  • tschabos schrieb:

    ich versuche die eulersche Zahl auf 10 Stellen nach dem Komma genau zu berechnen,
    jedoch weiss ich einfach nicht, wie ich die Abbruchbedingung formulieren soll, damit die Berechnung bei 10 Stellen nach dem Komma stoppt.

    Hallo tschabos,

    das hat mehr mit Mathematik als mit Programmieren zu tun. Mal angenommen, das Programm befindet sind in der Schleife an der Stelle i. Jetzt möchte man feststellen ob man noch weiter als ein vorgegebenes delta vom exakten Wert von e entfernt ist. Dafür stelle ich die Reihenentwicklung für den Rest auf; also das was auf die Summe noch aufgeschlagen werden wird:
    rest = k/(i+1) + k/((i+1)(i+2)) + k/((i+1)(i+2)(i+3)) + ...
     = k * (1/(i+1) + 1/((i+1)
    (i+2)) + 1/((i+1)(i+2)(i+3)) + ...)

    Die Aufgabe besteht nun darin, abzuschätzen, ob rest < delta ist. Ohne Kenntnis von e ist die Berechnung von rest schwierig, aber ich kann eine zweite Reihe aufstellen - derart
    rest2 = k * (1/(i+1) + 1/((i+1)(i+1)) + 1/((i+1)(i+1)*(i+1)) + ...)
    vergleicht man die Summanden von rest und rest2, so ist klar, dass rest2>rest ist. Gleichzeitig kann ich rest2 berechnen, da es sich hier um eine geometrische Reihe handelt.
    rest2 = k * ( 1/(i+1) /( 1 - 1/(i+1) ) ) = k/i;

    Folglich muss man nur noch prüfen, ob k/i<=delta ist, da rest2=k/i und rest<rest2 muss dann auch rest<delta gelten.

    Das delta postuliere ich mal auf 1/2*10^(-nk), wenn es auf nk Nachkommastellen genau sein soll.
    Hier ein kleines Demoprogramm ...

    #include <iostream>
    #include <iomanip> // setprecision, setw
    #include <cmath> // pow, exp
    
    // --   berechnet e auf nk Nachkommastellen genau
    double berechne_e( int nk )
    {
        double k = 1.0; // k_0 = 1/(0!) = 1
        double sum = k;
        const double delta = 0.5 * std::pow( 10.0, -nk );
        for( int i=1; ; ++i )
        {
            k /= i; // identisch zu k = (1/double(i) * k)
            sum += k; // identisch zu sum = (sum + k);
            if( k <= delta * i ) // entspricht k/i <= delta, wenn i>0
                break;
        }
        return sum;
    }
    
    namespace math
    {
        const double e = std::exp(1.0);
    }
    
    int main()
    {
        using namespace std;
        cout << "nk Naeherung         exakt" << endl;
        for( int nk=1; nk<15; ++nk )
        {
            double e = berechne_e( nk );
            // die Ausgabe geschieht auf nk+1 Nachkomastellen genau
            cout << setw(2) << right << nk << " " << fixed << setprecision(nk+1) << setw(nk+3) << left << e << setw(15-nk) << " " << math::e << endl;
        }
        return 0;
    }
    

    .. und dessen Ausgabe mit jeweils einer Nachkommastelle mehr, so dass man den Unterschied sieht:

    nk Naeherung         exakt
     1 2.71              2.72
     2 2.717             2.718
     3 2.7181            2.7183
     4 2.71825           2.71828
     5 2.718279          2.718282
     6 2.7182815         2.7182818
     7 2.71828180        2.71828183
     8 2.718281826       2.718281828
     9 2.7182818283      2.7182818285
    10 2.71828182845     2.71828182846
    11 2.718281828458    2.718281828459
    12 2.7182818284590   2.7182818284590
    13 2.71828182845904  2.71828182845905
    14 2.718281828459043 2.718281828459045
    

    Gruß
    Werner


Log in to reply