Als Teil eines Informatikprojekts erhielt ich die Aufgabe, die Eulersche Zahl zu programmieren. Dies musste in einer Methode erfolgen, und (a) sollte bis zum 1000. Reihenglied berechnet werden, und (b) bis der neue Summand < 1 * 10^-13 wurde. Das Programmieren der Eulerschen Zahl war für mich kein Problem - ich dachte Teilaufgabe (a) und (b) waren nicht zu schwierig. Jedoch bin ich auf einige Probleme gestossen. Wäre absolut super, wenn jemand helfen könnte!
So sieht mein Code aus, das Problem erkläre ich gleich.
public class EulersNumber
{
public static void main(String[] args) //Euler'sche Zahl im Ausdruck
{
double e = 1;
double j = 0;
double k = 0;
System.out.println("Die Eulersche Zahl lässt sich mit der Formel (1 / k!) berechnen: "+
"
e: "+ euler(j,k)); //e = 1.0
}
public static long factorial(long number) //Die Fakultät des Nenners berechnen
{
if (number < 2)
{
return 1;
} else
{
return number * factorial(number-1);
}
}
public static double euler(double j, double k)// Euler'sche Zahl: Methode
{
double e = 1;
for (long i = 1; i < 66; i++)
{
j = (double)(factorial(i));
k = 1 / j;
e += k;
}
return e;
}
}
Hier wird die Eulersche Zahl bis zum 66. Glied berechnet. Die ausgegeben Zahl ist zwar richtig, (2.718…), aber dies erfüllt die Aufgabe nicht. Der Schleifenzähler ist als Datentyp long definiert…falls ich den Wert erhöhe, heisst es in der Ausgabe " e = Infinity". 66 ist das Höchste, das ich eingeben kann. In meinen Gedanken, hätte man anstatt 66, 1000 eingesetzt, und so wäre es fertig gewesen. Aber leider nicht…
Ich dachte mit „BIG.Integer“ könnte es funktionieren, jedoch verstehe ich nicht wie ich BigInt in die for-Schleife schreiben kann. Noch nie BigIn durchgenommen, und meine Recherche im Internet half leider nicht. Bitte um Hilfe!! Danke vielmals fürs Lesen!
PS: ist mein erster Post, falls ich etwas falsch mache, bitte warnen! Danke.
Wenn schon, dann mit BigDecimal, weil du ja nicht mit ganzen Zahlen arbeitest. Dabei ist das kein primitiver Datentyp, sondern eine Klasse, bei der dann alles über Methoden läuft, z.B. heißt es dann nicht mehr a + b, sondern a.add(b).
In der Schleife kann ruhig long stehen bleiben. DER Wert muss ja nur bis 1000 laufen. Das Problem ist die factorial-Methode: Die muss einen long bekommen, aber einen BigInteger zurückliefern (weil der Wert ja riesig werden kann).
EDIT> @Landei Für die Fakulatät würde man schon BigInteger verwenden… <EDIT
Das Ergebnis als double wird ggf. auch nicht reichen. Insbesondere kann man man einen double ja nicht durch einen BigInteger teilen. Irgendwo wird da dann vermutlich sowas stehen wie
...
BigInteger f = factorial(i);
e = e.divide(new BigDecimal(f));
...
Ja, für die Fakultät selbst könnte man BigInteger nehmen, aber ich denke, der TO ist schon durch BigDecimal ausreichend verwirrt.
Da es gar nicht so einfach ist, das halbwegs korrekt hinzubekommen, insbesondere mit dem Scaling, poste ich entgegen allen guten Vorsätzen doch mal Code:
import java.math.BigDecimal;
public class Euler {
public static BigDecimal e(int steps, int scale) {
BigDecimal fac = BigDecimal.ONE.setScale(scale);
BigDecimal e = BigDecimal.ONE.setScale(scale);
for(int i = 1; i < steps; i++) {
fac = fac.multiply(BigDecimal.valueOf(i));
e = e.add(BigDecimal.ONE.setScale(scale).divide(fac, BigDecimal.ROUND_HALF_EVEN));
}
return e;
}
public static void main(String[] args) {
System.out.println(e(1000, 500));
}
}
Ob das nun wirklich so genau ist, sei mal dahingestellt, die 500 Stellen sind völlig aus der Luft gegriffen.
Ich hab das mal genau so nachprogrammiert wie du oben:
if (n < 2) {
return 1;
}
return n * fak1(n - 1);
}
private static double euler1() {
double e = 0;
for (int i = 0; i < 10; i++) {
e += 1.0d / fak1(i);
}
return e;
}
private static double euler2() {
double e = 0;
for (int i = 0; fak1(i) > 0; i++) {
e += 1.0d / fak1(i);
}
return e;
}
public static void main(String[] args) {
System.out.println(euler1());
System.out.println(Math.E);
System.out.println("");
System.out.println(euler2());
System.out.println(Math.E);
}```
Ausgabe:
2.7182815255731922
2.718281828459045
2.7182818284590455
2.718281828459045
Daran blickt man, das die Ausgabe wirklich schon sehr genau ist, mit dem int und long.
Du müsstest jetzt (Konjunktiv), wie Landei, richtige Klassen, Objekte und Methoden (also BigDecimal und so etwas) dafür finden, und könntest (wieder Konjunktiv) das dann 1-zu-1 übernehmen.
Da du den Anfang schon hattest, hab ich dir auch nicht zu viel verraten.
Allgemein blickt es schon nach Hausaufgaben aus, die wir für dich übernehmen. AAAABER: [Is there a method that calculates a factorial in Java? - Stack Overflow](http://stackoverflow.com/questions/891031/is-there-a-method-that-calculates-a-factorial-in-java) [Fast Factorial Functions](http://www.luschny.de/math/factorial/FastFactorialFunctions.htm) und das auch nochmal mit der "java euler number".
Mit dem rekursiven Berechnen der Fakultät sollte man hier sehr aufpassen.
Mit 1000 mag das ganze noch laufen aber mit 2000 kann das ganz schnell in die Hose gehen, da einem dann der Speicher ausgehen kann.
Besser ist hier eine Schleife zu benutzen, so schön und elegant Rekursion auch sein mag.
Und wenn eine Schleife verwendet wird dann kann man es so machen wie Landei es tut und die Fakultät weiterentwickeln, weil man sonst bei Tausend Berechnungen
1000 mal die Fakultät von 1 berechnet,
999 mal die Fakultät von 2 berechnet,
998 mal die Fakultät von 3 berechnet,
usw.
Damit ist die Existenz der Funktion factorial sehr in Frage zu stellen.
[QUOTE=Landei]Ob das nun wirklich so genau ist, sei mal dahingestellt, die 500 Stellen sind völlig aus der Luft gegriffen.[/QUOTE]Da mach dir mal keine Sorgen. Nach der Berechnung des 452. Gliedes ist Euler schon auf 1000 Stellen genau. Darauf kommt man, wenn man e vor der (Neu)Berechnung in gewünschter Genauigkeit in eOld speichert und e dann so oft berechnen lässt, bis es sich von eOld nicht mehr unterscheidet.
public class Euler {
public static BigDecimal e(int scale) {
BigDecimal fac = BigDecimal.ONE;
BigDecimal e = BigDecimal.ONE;
BigDecimal t = BigDecimal.ONE;
BigDecimal eOld = BigDecimal.ZERO;
while(e.compareTo(eOld) != 0) {
fac = fac.multiply(t);
t = t.add(BigDecimal.ONE);
eOld = e;
e = e.add(BigDecimal.ONE.setScale(scale).divide(fac, BigDecimal.ROUND_HALF_EVEN));
e.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
}
System.out.println(t);
return e;
}
public static void main(String[] args) {
System.out.println(e(1000));
}
}```Das 1000. Reihenglied wird erst ab der 2561. Nachkommastelle benötigt.
public static BigDecimal e(int n, int scale) {
BigDecimal one = BigDecimal.ONE.setScale(scale);
BigDecimal current = one;
for(int k = n - n % 2; k > 0; k-=2) {
current = one.add(one.divide(current, BigDecimal.ROUND_HALF_EVEN));
current = BigDecimal.valueOf(k).setScale(scale).add(one.divide(current, BigDecimal.ROUND_HALF_EVEN));
current = one.add(one.divide(current, BigDecimal.ROUND_HALF_EVEN));
}
return one.divide(current, BigDecimal.ROUND_HALF_EVEN).add(one).add(one);
}
Lesbarer Quellcode sieht anders aus. Ansonsten ist der Code schön kurz und wahrscheinlich schnell. Also eher für eine Bibliothek geeignet, als für den Nutzen in einer Hausaufgabe.
[ot]
Viel von der nicht-Lesbarkeit kommt daher, dass Code, der BigDecimal enthält, eben nicht besonders lesbar ist. Man könnte da sowas wie final int mode = BigDecimal.ROUND_HALF_EVEN;
rausziehen oder so, aber das wäre eher Kosmetik. So sehr ich Operatorenüberladung “im Allgemeinen” kritisiere: Für so eine Klasse wäre die teilweise schon praktisch.
[/ot]
Und der Rest der unlesbarkeit kommt wohl daher, dass dies die Implementierung einer Kettenbruchdarstellung und nicht der unendlichen Reihe ist.
Trotz gleicher Genauigkeit bei der Berechnung sollte sich das Ergebnis bei der gleichen Anzahl Iterationen also unterscheiden.
*** Edit ***
Eine Implementierung der Reihe als “Tröpfelalgorithmus” sollte eigentlich auch ziemlich einfach sein.
Siehe hier.
[QUOTE=Landei]Was haltet ihr hiervon?[/QUOTE]Bis auf den bereits angesprochenen Mangel Lesbarkeit, führe ich dazu noch mal ins Feld, dass es für eine solche Berechnung unnötig ist, die Anzahl der Reihenglieder und die Anzahl der gewünschten Nachkommastellen gleichzeitig zu übergeben.
Aus der Anzahl der zu berechnenden Nachkommastellen ergibt sich die Anzahl der zu berechnenden Reihenglieder und umgekehrt.
An die Anzahl der Nachkommastellen kommt man mit „log10((n-1)!)“ - wobei n natürlich die Anzahl der Reihenglieder ist. (Nach ersten Überlegungen meinerseits jedenfalls )
Mein Code oben gibt btw. ein Reihenglied zu viel aus, denn es wird ja noch mal 1 aufaddiert, wenn die Abbruchbedingung bereits zutrifft.