Sinus ohne Math.sin() berechnen

#1

Guten Abend,

ich hab für Informatik die Aufgabe bekommen einen Näherungswert für den Sinus ohne die Funktion Math.sin() berechnen. Dafür habe ich folgende Formel bekommen:

[TEX]\sum_{n=0}^\infty (-1)^n\frac{x^{2n+1}}{(2n+1)!}[/TEX]

Nun zuerst erschien mir dies nicht weiter schwierig und habe folgenden Code geschrieben:

    private static final double EPSILON = 0.0000001;

    public static long f(long n) { 
        // diese Methode berechnet natürlich die Fakultät von n
        return (n == 0) ? 1 : n * f(n-1);
    }    

    public double sinus(double x) {
        double zaehler;
        double nenner;
        double alt = 0.0;
        double neu = 0.0;

        for(int i = 0; ; i++) {
            zaehler = Math.pow(x, (2*i)+1);
            nenner = f((2*i) + 1);
            neu += ((-1)^i) * (zaehler/nenner);
            if(Math.abs(alt-neu) < EPSILON)
                break;
            alt = neu;
        }
        return neu;
    }

Wie ihr seht will ich die Summe durch eine for-Schleife darstellen, die abbricht, wenn mein Ergebnis die entsprechende Genauigkeit erreicht.
Außerdem berechne ich Zähler und Nenner des Bruchs vorher, damit ich keinen super langen Ausdruck dort stehen habe.
Beim Ausführen des Codes erhalte ich eine Fehlermeldung(eine entsprechende main Methode dafür existiert). erhalte ich eine Fehlermeldung.
Beim Debuggen habe ich festgestellt, dass die Schleife gar nicht verlassen wird und das sich mein Ergebnis in der Methode einem Vergleichswert der Methode Math.sin() annähert.
Wie es scheint mache ich wohl etwas bei der Berechnung falsch aber ich sehe einfach nicht was.
Ursprünglich sollte ich die Funktion sogar komplett ohne Funktionen des Moduls Math implementieren aber ich dachte mir ich entwickle erst einmal mit den Methoden und ersetzte diese später durch meinen eigenen Code.

lg

Martin

#2

das ist ein unerhörter Start in Programmierung, aber immerhin ein guter zum zügigen Lernen daraus :wink:

für hier im Forum:
was ist denn deine main, mit welchem Wert testet du? kann wichtig sein, bei x = 0.2 jedenfalls keine Fehler,
und welche Fehlermeldung bekommst du? kein Grund sie nicht zu nennen

zum Programm:
warum so sorglos, es gibt doch System.out.println() um den Programmablauf auch zu prüfen,
gib in der Schleife i aus, den Wert für zaehler und nenner, wenn nötig die Differenz aus alt und neu usw.

was ist wohl die Fakultät von 10, von 50, von 1000?
mit solchen Werten hantiert das Programm, es steht in deiner Verantwortung davon zu wissen und es verantwortlich zu steuern :wink:

#3

Erstmal vielen dank für die Antwort.
Ein Fehler hab ich z.b. schon gefunden. ^ ist nicht der Operator für die Potenz sondern für XOR. Hätte ich eigentlich wissen müssen, kam erst letztens in der Vorlesung dran :smiley: . Wie gesagt benutze ich eigentlich keine println() sondern den Debugger von IntelliJ. Der ist wirklich sehr komfortabel und zeigt mir die Werte jeder Variable direkt im Quellcode an.

Bei der Fakultät dachte ich eigentlich ich hätte mit long genug Spielraum, da ich nicht erwartet hatte, dass die Schleife so viele Durchläufe braucht.

Im übrigen ist mir gerade Aufgefallen, dass ich für die Testdaten die falsche Einheit benutzt habe. Ich hatte die Werte in Grad und nicht in rad angegeben. Ich habe übrigens einfach mal mit dem Wert 30.0 getestet. Das hier der Typ long schon nicht mehr ausreicht, hätte ich mir eigentlich denken können ^^. Ich denke hier ist es auch einfache alle Werte größer Pi abzufangen anstatt die etwas unhandliche Klasse Big Integer zu benutzen.

#4

Es sei noch gesagt, vielleicht liegt daran der Fehler, dass du schreibst (-1)^i. Hier verwendest du bitweise XOR.
Potenzieren geht, wie schon selbst benutzt mit Math.pow, oder in dem Fall if/else oder … (viele weitere Möglichkeiten…)

#5

Stimmt. Außerdem könnte die Fakultät selbst mit long überlaufen.

Eine gute Strategie wäre, nicht immer alles “neu” zu berechnen, sondern aus dem letzten Reihen-Wert den nächsten, was hier besonders schön geht:

    private static final double EPSILON = 0.0000001;

    public static double sin(double x) {
        double value = x;
        double sum = x;
        for(int n = 1; Math.abs(value) > EPSILON; n++) {
           value *= -x*x/(2*n+1)/(2*n);
           sum += value;
        }
        return sum;
    }
#6

Ja, das ((-1)^i) ist wohl der Knackpunkt. Das schreibt ein Mathematiker gerne hin, weil’s so ““elegant”” aussieht (gut, darüber braucht man nicht zu streiten).

Für Informatiker gibt’s ähnlich “elegante” Lösungen. Sowas wie
-1+((i & 1)*2)
oder gleich
-1+((i & 1)<<1)
:smiley:

Oder eben ein un-elegantes

if ((i % 2) == 0) { gerade Zahl }
else { ungerade Zahl }

das man dann entsprechend verrechnet.