# Sinusreihe berechnen



## xxxsickxxx (9. Nov 2013)

Bräuchte Hilfe bei einem Beispiel.
Nach Eingabe der Genauigkeit g und x soll der Computer den Wert sin(x) durch die Sinus-Reihe berechnen:
Die Formel der Sinusreihe ist im Anhang.


----------



## diggaa1984 (9. Nov 2013)

wo genau hast du Probleme?


----------



## xxxsickxxx (9. Nov 2013)

Also die Eingabe der Zahlen ist kein Problem.
Fakultät berechnen ist auch kein Problem.


----------



## redJava99 (9. Nov 2013)

Die Aussichten auf Hilfe sind immer höher, wenn man einen Ansatz präsentiert.
Z.B. Code-Teile, die kein Problem sind. Den Rest kannst du z.B. als PseudoCode einbauen oder beschreiben, was du tun würdest, und woran es hängt.


----------



## xxxsickxxx (9. Nov 2013)

Mein Code sieht momentan so aus


```
package C_BSP_Seite12;


import java.lang.Math;
public class Sinus 
{
	/**
	 * @author Daniel Schaffer
	 * Class:1ABIF
	 * Date:19.10.2013
	 */
	 
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
// Nach Eingabe der Genauigkeit g und x soll der Computer den Wert sin(x) durch die Sinus-Reihe berechnen:
		
		//Eingabe der Werte g und x
		double g = Double.parseDouble(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Genauigkeit g ein!" ));//Genauigkeit
		int x = Integer.parseInt(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Zahl x ein!" )); // Zahl x
		double sin; // sinus
		int n=3; // potenz
		double y; // ergenis vom potenzieren
		double fak=1;// fakultät
		int i=1; //Zählvariable
		double ab; //abbruch
		double fak2=1;//fakultät 2
		double erg=0;
		sin=x;
		do
		{
			y = Math.pow(x, n); //Potenzieren
			//System.out.println("y:"+y);
			for(int j=2;j<=n;j++)// Fakultät berechnen
			{
				fak*=j;								
			}
			n+=2;
			//System.out.println("fak:"+fak);
			if(i%2!=0)
			{	
				sin-=(y/fak);
				//System.out.println("sin-:"+sin);
			}
			else
			{
				sin+=(y/fak);
				//System.out.println("sin+:"+sin);
			}
		ab=(2*i)-1;
		//System.out.println("ab:"+ab);
		x= Math.abs(x);
		i++;
		double ab1=Math.pow(x,ab);	
		//System.out.println("ab1:"+ab1);
		
		for(int j=2;j<=ab;j++)// Fakultät berechnen
		{
			fak2*=j;								
		}
		//System.out.println("fak2:"+fak2);
		erg= (ab1/fak2);
		System.out.println("erg:"+erg);
		
		fak=1;
		fak2=1;
	}		
		while(erg<=g);
		
		System.out.println(sin);
			
		
		
	}
}
```


----------



## redJava99 (9. Nov 2013)

Ouh, das sieht noch etwas chaotisch aus.

Also, hier einige Tips:
- x kann durchaus auch eine Kommazahl sein --> double
- Finde den allgemeingültigen mathematischen Ausdruck, der die sinusreihe beschreibt (mit n, das ist nämlich die Anzahl der Summanden).
- Teile das Problem in Teilprobleme auf:
  --> Ein Problem ist das Berechnen der Fakultät (Du kannst nicht für jede mögliche Zahl die Fakultät hart gecodet berechnen... --> Methode schreiben

```
private static int fak(int k){
//berechne fakultät von k
}
```
  --> Anderes Problem: Abbruchbedingung prüfen. Auch hier kann man eine Methode entwickeln, die in Abhängigkeit von x, g und n prüft, ob sin(x) hinreichend genau berechet wurde.

Damit gewinnst du einiges an Übersicht. Und du brauchst auch nicht unmengen an Werten in variablen zwischenspeichern.


----------



## xxxsickxxx (10. Nov 2013)

Danke für die infos.
Habe meinen Code umgeändert aber habe noch immer ein problem damit.
Das mit der methode entwicklen für die Abbruchbedingung verstehe ich nicht.

```
package C_BSP_Seite12;


import java.lang.Math;

import javax.swing.JOptionPane;
public class Sinus 
{
	/**
	 * @author Daniel Schaffer
	 * Class:1ABIF
	 * Date:19.10.2013
	 */
	
	private static double fak (double i)
	
	{
	 double fak=1;
	 for(double j=2;j<=i;j++)// Fakultät berechnen
		{	
		fak*=j;
		}

	return fak;
	}
	
	
	 
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
// Nach Eingabe der Genauigkeit g und x soll der Computer den Wert sin(x) durch die Sinus-Reihe berechnen:
		
		//Eingabe der Werte g und x
		double g = Double.parseDouble(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Genauigkeit g ein!" ));//Genauigkeit
		double x = Double.parseDouble(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Zahl x ein!" )); // Zahl x
		double sin; // sinus
		int n=1; // Anzahl des Schleifendurchlaufes
		double y; // ergenis vom potenzieren
		double i=3; //Anfangswert 
		double erg=0; // Ergebnis der Abbruchbedingung
		sin=x;
		do
		{
			y = Math.pow(x,i); //Potenzieren
			if(i%2!=0)	//prüfen ob gerader oder ungerader Schleifendurchlauf
			 {	
			  sin-=(y/fak(i));
			 } 
			else
			 {
			  sin+=(y/fak(i));
			 }
		 double xabs= Math.abs(x); // absolut wert anzeigen
		 int z=(2*n)-1;
		 double ab1=Math.pow(xabs,z); //potenz berechnen
		 erg= (ab1/fak(z)); // Ergebnis der Abbruchbedingung ausrechnen.
		 n++; // Schleifendurchlauf um 1 erhöhen
		 i+=2; // Anfangswert um 2 erhöhen
		}		
		while(erg<=g);

		JOptionPane.showMessageDialog(null, "sinus(x) "+sin, "Sinus",JOptionPane.INFORMATION_MESSAGE);
	}
}
```


----------



## redJava99 (10. Nov 2013)

Hi,

sieht schon besser aus. Ein Problem fällt direkt auf: Du verwendest i und n als eine Art Durchlauszähler. i als Exponent hängt aber unmittelbar von n ab.
Zum einen erzeugst du mit der Verwendung diese i also Redundanz, zum anderen wird die Bedingung

```
i%2 != 0
```
 immer erfüllt sein (wegen i = 3, 5, 7, ...).
Also: Lass i verschwinden und berechne den Exponenten aus n. Und wegen n = 1, 2, 3 ... kannst du dann hervorragend auf 
	
	
	
	





```
n % 2 != 0
```
 prüfen.

Mit der Methode für den Endebedingung meinte ich, die Zeuilen 54 bis 59 in eine Methode auszulagern, damit klar ist, dass das mit der eigentlichen Berechnung nichts zu tun hat.

Z.B. 
	
	
	
	





```
private static boolean isEnd(int g, int x, int n){
//...
}
```

Und entsprechend in der Schleife die Abfrage

```
do{
//...
}while(! isEnd(g, x, n));
```

Nochwas am Rande: Die Datentypen sind etwas willkürlich. Die Fakultät ist nur für natürliche Zahlen definiert. Demnach ist für den Parameter und den Rückgabewert der Datentyp int die bessere Wahl.


----------



## stg (10. Nov 2013)

redJava99 hat gesagt.:


> Die Fakultät ist nur für natürliche Zahlen definiert.



Du kannst die Fakultät auch gescheit für reelle Zahlen (direkt über die Gaußsche Gammafunktion) oder, wenn man etwas mehr Gehirnschmalz reinsteckt, auch für komplexe Zahlen noch gescheit und konsistent mit der Definition für natürliche Zahlen definieren.

Tut hier aber nichts zur Sache. Hier die Berechnung der Fakultät in eine extra Methode auszulagern, ist extrem ineffizient.

Lässt man eine Schleife über alle ungeraden natürlichen Zahlen laufen, so sind in jedem Schleifendurchlauf lediglich zwei Multiplikationen, zwei Divisionen und eine Addition durchzuführen. 
Genauer: Wenn ich zb x^13 und 13! schon kenne, dann muss ich, um x^15 und 15! zu erhalten doch nur den ersten Wert mit x^2 multiplizieren und den zweiten durch 14*15 dividieren und nicht komplett alles von vorne an neu berechnen.
Auf diese Art und Weise bleibt die Gesamtlaufzeit lnear und explodiert nicht in den exponentiellen Bereich. Das ist echt tötlich, schon für 'relativ kleine' Werte deiner Schleifenvariable.


Die gewünschte Genauigkeit ist übrigens erreicht, wenn der nächste Summand, den du deiner Summe hinzuaddieren würdest betragsmäßig kleiner der geforderten Genauigkeit ist. Das folgt direkt aus einer Restgliedabschätzung der Taylorentwcklung des Sinus um x=0.


----------



## redJava99 (10. Nov 2013)

Was die Effizienz betrifft, hast du theoretisch recht.
Allerdings ist die Reihenentwicklung ziemlich früh ziemlich genau, sodass man über n=10 (--> 19!) kaum hinaus kommt. Gegen ein Cachen bereits berechneter Werte spricht natürlch dennoch nichts.

Ich gehe in diesem Fall allerdings davon aus, dass es beim TE weniger auf Effizienz als auf grundlegendes Verständnis ankommt.


----------



## xxxsickxxx (10. Nov 2013)

Also das mit i und n hab ich angepasst.
Habe ich beim vorigen ausbessern vergessen.

Wenn ich die Abbruchbedingung auslagere wie du schreibst.Was muss ich dann als return wert zurückgeben?
Bei boolean sollte es true oder false sein oder liege ich hier falsch?
Meine Abbruchbedingung sieht so aus.
Stimmt diese methode?

```
private static boolean isEnd (double g,double x,int n)
	{
		 double xabs= Math.abs(x); // absolut wert anzeigen
		 int z=(2*n)-1;
		 double ab1=Math.pow(xabs,z); //potenz berechnen
		 double erg= (ab1/fak(z)); // Ergebnis der Abbruchbedingung ausrechnen.
		 return true;
		 
	}
```


----------



## redJava99 (10. Nov 2013)

Eine Methode, die ausnahmslos immer true zurückgibt, macht in den wenigsten Fällen Sinn.
Die Reihenentwicklung ist nur dann beendet, wenn dein _erg_ < g ist.


----------



## xxxsickxxx (10. Nov 2013)

Also muss die Methode false zurückgeben?

```
private static boolean isEnd (double g,double x,int n)
	{
		 double xabs= Math.abs(x); // absolut wert anzeigen
		 int z=(2*n)-1;
		 double ab1=Math.pow(xabs,z); //potenz berechnen
		 double erg= (ab1/fak(z)); // Ergebnis der Abbruchbedingung ausrechnen.
		 
		 return false;
		 
	}
```
aber wo vergleiche ich ob das Ergebnis kleiner als g ist?


----------



## redJava99 (10. Nov 2013)

> aber wo vergleiche ich ob das Ergebnis kleiner als g ist?


na, wenn du das Ergebnis fertig berechnet hast. Der Wert für g liegt dir ja auch vor (wurde übergeben).

Und nein, die Methode muss nicht einfach false zurückgeben. Sondern true _oder_ false, abhängig davon, wie der Vergleich von erg und g ausgeht.
Wenn _erg < g_ ist, true; sonst false.


----------



## xxxsickxxx (10. Nov 2013)

Okay dann sollte die Methode so aussehen:


```
private static boolean isEnd (double g,double x,int n)
	{
		 double xabs= Math.abs(x); // absolut wert anzeigen
		 int z=(2*n)-1;
		 double ab1=Math.pow(xabs,z); //potenz berechnen
		 double erg= (ab1/fak(z)); // Ergebnis der Abbruchbedingung ausrechnen.
		 System.out.println(erg);
		 if (erg<g)
		 {
			 return true;
		 }
		 else
			 {
			 return false;
			 }
		 
	}
```


----------



## redJava99 (10. Nov 2013)

Exakt. Oder kürzer: 
	
	
	
	





```
return erg < g;
```
Denn der Ausdruck 
	
	
	
	





```
erg < g
```
 liefert einen bool'schen Wert.


----------



## xxxsickxxx (10. Nov 2013)

Habe das alles angepasst.


```
package C_BSP_Seite12;


import java.lang.Math;

import javax.swing.JOptionPane;
public class Sinus 
{
	private static char[] isEnd;


	/**
	 * @author Daniel Schaffer
	 * Class:1ABIF
	 * Date:10.11.2013
	 */
	
	private static int fak (double i)
	
	{
	 int fak=1;
	 for(int j=2;j<=i;j++)// Fakultät berechnen
		{	
		fak*=j;
		}

	return fak;
	}
	private static boolean isEnd (double g,double x,int n)
	{
		 double xabs= Math.abs(x); // absolut wert anzeigen
		 int z=(2*n)-1;
		 double ab1=Math.pow(xabs,z); //potenz berechnen
		 double erg= (ab1/fak(z)); // Ergebnis der Abbruchbedingung ausrechnen.
		// System.out.println(erg);
		 return erg < g;
		 
	}
	
	 
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
// Nach Eingabe der Genauigkeit g und x soll der Computer den Wert sin(x) durch die Sinus-Reihe berechnen:
		
		//Eingabe der Werte g und x
		double g = Double.parseDouble(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Genauigkeit g ein!" ));//Genauigkeit
		double x = Double.parseDouble(javax.swing.JOptionPane.showInputDialog( "Geben Sie die Zahl x ein!" )); // Zahl x
		double sin; // sinus
		int n=1; // Anzahl des Schleifendurchlaufes
		double y; // Ergebnis vom potenzieren
		double i=3; //Anfangswert 
		sin=x;
		do
		{
			y = Math.pow(x,i); //Potenzieren
			if(n%2!=0)	//prüfen ob gerader oder ungerader Schleifendurchlauf
			 {	
			  sin-=(y/(fak(i)));
			 } 
			else
			 {
			  sin+=(y/(fak(i)));
			 }
			isEnd(g,x,n);
		 	n++; // Schleifendurchlauf um 1 erhöhen
			i+=2; // Anfangswert um 2 erhöhen
			
		}		
		while(! isEnd(g,x,n));

		JOptionPane.showMessageDialog(null, "sinus(x) "+sin, "Sinus",JOptionPane.INFORMATION_MESSAGE);
	}
}
```
Stimmt dieser Code?


----------



## redJava99 (10. Nov 2013)

Das kannst du durch Einsetzen von ein paar Werten und vergleichen mit dem Taschenrechner selbst testen ;-)
Habe es eben mal für x=PI/2 und x=1 probiert - sieht gut aus.


----------



## xxxsickxxx (10. Nov 2013)

Oh ja mein Taschenrechner war verstellt.
Danke für deine Hilfe!

Funktioniert echt gut.

Besten Dank für deine Geduld.


----------



## xxxsickxxx (10. Nov 2013)

Wenn ich jedoch eine größere Zahl eingebe stimmt das Ergebnis nicht.

Laut Taschenrechner ist sin(10) -0,5440211 und laut programm -3.4708853816657174E8

Was kann hier der Fehler sein?


----------



## stg (10. Nov 2013)

Du hast einen Überlauf bei der Berechnung der Fakultät.

Hälst du dich an meinen Vorschlag (die von mir vorgeschlagene Lösung ist numerisch stabiler), dann sollte es auch für größere x funktionieren.

Edit:

Oder zunächst einmal: Verwende für die Berechnung der Fakultät einfach einen long / BigInteger oÄ, damit ist ja auch schon mal was gewonnen!


----------



## stg (10. Nov 2013)

```
long
```
 als Datentyp reicht auch noch nicht aus. Die 10 ist einfach schon zu weit vom Entwicklungspunkt der Sinusreihe entfernt :noe:

Mit long für die Fakultät solltest du bis Schätzungsweise x=6 ziemlich gute Ergebnisse bekommen, für größere x solltest du dich aber generell um eine andere Implementierung bemühen.


----------



## xxxsickxxx (11. Nov 2013)

Danke für die Info.


----------



## stg (11. Nov 2013)

Noch ein Vorschlag:
Du nutzt die Periodizität des Sinus aus und verschiebst dein x solang um Vielfache von 2π, bist der Wert von x zwischen -π und π liegt. Bei dieser Verschiebung bekommst du zwar auch wieder Ungenauigkeiten in deine Rechnung, die aber, falls du nicht wirklich hochpräzise Ergebnisse erhalten willst (aber dann würdest du vermutlich eh einen ganz anderen Ansatz nutzen müssen), kaum ins Gewicht fallen sollten. Du kannst es ja mal ausprobieren..


----------



## xxxsickxxx (11. Nov 2013)

Also die Genauigkeit reicht aus, da dieses Beispiel nur für die Schule ist.


----------

