Eigene Sinus und Cosinus funktionieren nicht
-
Heyo, ich habe probiert eine eigene Sinus und Cosinus Methode zu codieren ohne die Math Library zu nutzten. Nur meine beiden Methoden funktionieren teilweise, das Problem beliegt darauf, das die Werte 0, 1 tadellos bei beiden Methoden funktionieren, doch ab 1 weichen die Werte ab. Ich habe schon viel daran gekrübelt, doch ich komme momentan nicht ganz auf den Fehler.
Hier sind einmal meine beiden Methoden:
public static double sin(double x) { double sin = x; int odd = 3; for(int i = 0; i < 10; i++) { System.out.println(sin); if(i % 2 == 1) { sin += power(x, odd)/fakultaet(odd); } else { sin -= power(x, odd)/fakultaet(odd); } odd += 2; } return sin; } public static double cos(double x) { double cos = 1; int even = 2; for(int i = 0; i < 10; i++) { System.out.println(cos); if(i % 2 == 1) { cos += power(x, even)/fakultaet(even); } else { cos -= power(x, even)/fakultaet(even); } even += 2; } return cos; }
Ich hoffe auf baldige Rückmeldungen/Antworten
Lg Bruti
-
Deine Loops gehen überi
. Aber dasi
kommt in deiner Formel nur als vor, nicht aber in der Potenz oder der Fakultät. Will sagen, daspower(x, odd)/fakultaet(odd)
ist eine Konstante in deiner Summe. Du berechnest alsoMist, ich hatte erst das
odd += 2
übersehen.Du machst hier eine Taylorentwicklung um 0. Daher sind größere Abweichungen denkbar, je weiter du von der 0 weg bist. Du könntest auch im x= eine Entwicklung machen usw.
-
Taylorreihen sind übrigens denkbar ungeeignet, um numerische Entwicklungen durchzuführen. Abgesehen davon, dass sie ohnehin sehr langsam konvergieren, bekommt man auch Probleme dadurch, dass die Einzelterme unhandlich groß/klein werden. 13! passt schon nicht mehr in 32 Bit Integer. x^N kann je nach x auch sehr schnell explodieren oder implodieren. Weiterhin hat man dann bei der naiven Aufsummierung das Problem, dass man immer kleiner werdende Terme zu einer immer größer werdenden Zwischensumme addiert, was ungünstig ist, weil große Zahl + kleine Zahl eine große numerische Ungenauigkeit hat. Man müsste also besser umgekehrt bei den kleinen Termen anfangen, damit die Zwischensummen immer ungefähr von der gleichen Größe wie der nächste Summand sind.
Es würde mich überraschen, wenn du Sinus oder Cosinus mit int/double für eine ganze Schwingung in erkennbarer Form berechnen kannst.
In der echten Numerik benutzt man daher für die jeweilige Funktion spezialisierte Entwicklungen, oft kombiniert mit vorberechneten Stützstellen. Für trigonometrische Funktionen guck dir mal CORDIC an. Das ist sogar erstaunlich flexibel auf viele Familien von Funktionen anwendbar.
-
@MrBrutDev: Deine Implementierung ist außerdem sehr ineffizient. Es müssen
power
undfakultaet
in jedem Schleifendurchgang immer wieder komplett neu berechnet werden - besser ist eine iterative Implementierung (mathematische Formeln sind nicht immer programmiertechnisch effizient).
-
@SeppJ Erstmal danke für die Antwort!
Also ist es zu empfehlen einen BigInteger/BigDecimal zu nutzten?
Und was soll ich unter "immer kleiner werdende Terme" verstehen. Soll ich dann das x irgendwie erhöhen bzw. mit der Variable sin überschreiben?Lg Bruti
-
@Th69 Leider bin ich noch nicht so Erfahren, das ich das Umsetzen kann bzw. wie man es Umsetzen könnte, zudem wird das für die Übung/Beispiel nicht erwartet.
Lg Bruti
-
@MrBrutDev sagte in Eigene Sinus und Cosinus funktionieren nicht:
Also ist es zu empfehlen einen BigInteger/BigDecimal zu nutzten?
Und was soll ich unter "immer kleiner werdende Terme" verstehen. Soll ich dann das x irgendwie erhöhen bzw. mit der Variable sin überschreiben?Ich weiß nicht recht, wie du von einer Erklärung, die mit "Taylorreihen sind übrigens denkbar ungeeignet," beginnt, auf diese Verbesserungsideen kommst. Aber ja, mit arbitrary-precision Datentypen löst du die genannten Probleme. Mit Rechenkraft. Dann braucht es am Ende halt ein paar Sekunden, bis du eine erkennbare Sinuskurve berechnet hast. Wenn das dein Anspruch ist, dann nur zu.
"immer kleiner werdende Terme": Die Summe in der Taylorreihe besteht aus stetig kleiner werdenden Einzelsummanden. Weil die Fakultät im Nenner viel schneller wächst als die Potenz im Zähler. Das heißt, die Summe der ersten N Faktoren ist in der Regel viel größer als der N+1'te Faktor, den du dazu addieren musst. Das ist aber denkbar ungünstig bei Fließkommazahlen, weil beispielsweise float(1) + float(0.00000001) eben nicht 1.00000001 sein wird, sondern eher so etwas wie 1.00000000596. Oder schlimmstenfalls sogar wieder 1. Ich habe dieses Beispiel jetzt nicht exakt durchgerechnet, aber so ungefähr läuft das. Das heißt, bei Summen von Fließkommazahlen müssen für hohe Genauigkeit beide Zahlen ungefähr gleich groß sein, und bei großen Unterschieden kann man enorme Fehler bekommen, sogar 100% Fehler sind dann mit Leichtigkeit möglich.
BigDecimal hat dieses Problem nicht, auf Kosten der Berechnungsdauer und Speicheranforderung einer Addition. Die verlinkten spezialisierten Algorithmen haben dieses Problem aber generell nicht, auch nicht - oder gerade nicht - wenn man mit einfachen Fließkommaoperationen rechnet.
-
@SeppJ Okay, danke für die Erklärung. Ich werde mir die Taylorreihe dann mal besser anschauen, somit komme ich vielleicht auch selbst dann auf die Lösung.
Danke für die Rückmeldung!
Lg Bruti
-
@MrBrutDev sagte in Eigene Sinus und Cosinus funktionieren nicht:
@SeppJ Okay, danke für die Erklärung. Ich werde mir die Taylorreihe dann mal besser anschauen, somit komme ich vielleicht auch selbst dann auf die Lösung.
Ohne Worte…
-
Schau mal auf https://www.wolframalpha.com/input?i=series+sin+x+to+order+20
Da kannst du sehen
a) wie groß die Nenner werden
b) dass die die Kurven trotzdem bei "größeren" weglaufen.Was macht eigentlich deine Fakutät? Du fängst mit 3! und machst 10 Schritte, wobei du immer 2 addierst. Also 3!, 5!, ... 21! (wenn ich mich nicht verzählt habe). 21! sind schon 51090942171709440000. Was für einen Rückgabetyp hat deine Funktion?