mikrocontroller.net

Forum: PC-Programmierung Sinus Rek


Autor: Bjoern- (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich soll ein Programm schreiben, welches rekursiv den Sinus 
mittels einer Summe ermittelt. Das Hab ich auch geschrieben, allerdings 
ist das ganze irgendwie ziemlich Bug-ig vielleicht habt ihr eine Idee 
wie ich das Programm verbessern kann. Danke schonmal im Vorraus. Ach ja 
ich will keine Lösung sondern nur ein paar Verbesserungsvorschläge.

Autor: Bjoern- (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Das Programm

Autor: T.Stütz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mir kurz dein PGM angeschaut, folgende Dinge sind mir 
aufgefallen:
- dir fehlt vielleicht nur ein "#include <math.h>"
- die erste "while (sin != sinold)" Schleife kann dazu führen das das
  Programm in einer Endlosschleife hängt (drandenken double!)
  besser ist da vielleicht "while (0.0001 < fabs(sinold - sin))"
  (hier halt auf 4 Stellen nach dem Komma)

allerdings wäre es schön mal zu wissen was das Programm falsch macht ?
(fehlerhafte ausgabe, Stackoverflow etc..)

Gruss

Autor: Bjoern- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja also das Programm läuft einfach ewig weiter also dauerschleife. Die 
bedingung ist leider, das das ganze ohne math.h ablaufen soll.

Autor: Bjoern- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bzw. wenn ich es mit float mache steht da dann irgendwann (allerdings 
für größere x werte z.B. "44.08246", "1.IND00" in der Ausgabe keine 
ahnung was das bedeutet. Und das Ergebnis stimmt einfach nicht. bei dem 
Wert aus meinem Programm kommt wenigstens ansatzweise das richtige raus 
bloß mit falschem vorzeichen

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> welches rekursiv den Sinus mittels einer Summe ermittelt.

Fang erst mal damit an, eine rekursive Funktion aufzusetzen.
Bis jetzt ist da keine einzige Funktion drin, geschweige
denn eine rekursive. Selbst wenn das Pgm laufen würde
und richtige Ergebnisse bringen würde, würdest du von
mir 0 Punkte kriegen, weil das eine Übung zum Thema
Rekursion ist. Ohne Rekursion -> Thema verfehlt.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hinzufügen möchte ich noch, dass dein grundsätzliches
Vorgehen, wie du die Formel umsetzt und von einem Bruch
zum nächsten weiterrechnest schon sehr gut ist.
Nur halt nicht rekursiv.

Autor: Bjoern- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mhh, dann habe ich das mit der rekursiven Funktion noch nich ganz 
verstanden, könntest du mir vielleicht einen Tip geben, was ich da genau 
beachten muss?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rekursionen sind am Anfang ungewohnt.
Das Prinzip ist, dass man quasi die Funktion selbst benutzt
um Teilergebnisse zu berechnen.

Ein Beispiel:
Die willst die Fakultät von irgendetwas berechnen.
Wie wir wissen geht das ganz einfach:
Um die Fakultät von n zu berechnen, berechnet man die
Fakultät von n-1 und multipliziert dieses Ergebnis mit n.
Wie berechnet man aber die Fakultät von n-1?
Nach dem gleichen Schema! Nennen wir n-1 einfach mal m,
dann ist die Frage: Wie berechnet man die Fakultät von
m? Indem man die Fakultät von m-1 berechnet und dieses
mit m multipliziert. Wie berechnet man die Fakultät  von
m-1? Gleiches Schema: ...
Das geht jetzt immer so dahin. Wichtig ist: Die urdprüngliche
Aufgabenstellung war die Fakultät von n zu berechnen. Dies
hat als Ergebnis gebracht, dass wir die Fakultät von n-1
berechnen müssen. Dafür müssen wir wiederrum die Fakultät
von n-2 berechnen ( m war ja n-1, m-1 ist daher n-2) und
das geht jetzt immer so dahin. Wir müssen die Fakultät von
n-3, n-4 usw. berechnen.
D.h. aber auch: Die Aufgabe ist immer leichter zu lösen.
Die Fak von n zu berechnen ist schwer, die Fak. von n-1
ist ein bischen leichter, n-2 ist noch leichter usw.
bis wir letztendlich angelangt sind, die Fak. von n-n
zu berechnen. Nun, n-n ist aber 0, und die Fak. von
0 kennen wir: Die ist 1.

Ein wesentlicher Punkt in einer Rekursion ist es meist,
dass ein Zahlenwert immer kleiner und kleiner wird, bis
man für einen Zahlenwert von 0 oder 1 das Ergebnis sofort
angeben kann.

Wir haben also:
I)   Fak( 0 ) -> 1
II)  Fak( n ) -> n * Fak( n - 1 )

Wenn du dich mal daran versuchst, dann berechne mal als Aufgabe:
Fak( 4 ):

a) Fak( 4 ) ->  4 * Fak( 3 )      nach Regel II
b) Fak( 3 ) ->  3 * Fak( 2 )      nach Regel II
c) Fal( 2 ) ->  2 * Fak( 1 )      nach Regel II
d) Fak( 1 ) ->  1 * Fak( 0 )      nach Regel II
e) Fak( 0 ) ->  1                 nach Regel I

Das  Ergebnis von e) wird jetzt rückwärts in d) eingesetzt

e) in d):  1 * Fak( 0 ) -> 1 * 1 ->  1;  Fak( 1 ) ist also 1
d) in c):  2 * Fak( 1 ) -> 2 * 1 ->  2;  Fak( 2 ) ist also 2
c) in b):  3 * Fak( 2 ) -> 3 * 2 ->  6;  Fak( 3 ) ist also 6
b) in a):  4 * Fak( 3 ) -> 4 * 6 -> 24;  Fak( 4 ) ist also 24

das Endergebnis lautet daher: Fak( 4 ) := 24

Wie macht man das in C:
Man setzt die Rekursionsregeln von oben um:

int fac( int n )
{
  if( n == 0 )
    return 1;
  else
    return n * fac( n-1 );
}

Ein Wesen jeder Rekursion ist es daher, dass es praktisch
immer einen sog. 'Trivialfall' gibt. Ein Fall der so
einfach ist, dass man das Ergebnis sofort angeben kann.
In diesem Beispiel war das die Fakultät von 0. Dieser Trivial-
fall ist wichtig, denn er stoppt die Kette der Aufrufe die
die Funktion von sich selbst macht und startet das 'Rückwärts
einsetzen' der Ergebnisse.

Nun wird man in der Praxis natürlich eine Fakultät nicht
mit Rekursion lösen, da ist eine simple For Schleife wesentlich
einfacher. Aber es gibt Problemstellungen, die rekursiv wesentlich
einfacher zu lösen sind als mit Schleifen: Das Problem der
Towers of Hanoi ist zb. so eines.

Ein anderes Beispiel:
Eine rekursive Funktion soll die Summe aller Zahlen 1/n
von 1 bis n berechnen: Also

              n    1
  f(n) := sum   ( ---- )
              1    n

Um also f( n ) zu berechnen, genügt es f(n-1) zu berechnen zu diesem
Ergebnis 1/n zu addieren. Für den Fall n gleich 1, ist das besonders
Einfach, da ist das Ergebnis 1

  f( 1 ) -> 1
  f( n ) -> 1/n + f( n - 1 )

oder als Code:

 double Sum( int n )
 {
   if( n == 1 )
     return 1;
   else
     return 1.0 / n  +  Sum( n - 1 );
 }

In deinem Beispiel wäre die Zahl die immer kleiner wird,
die Anzahl der Glieder die zu berechnen ist.

Deine Funktion kriegt auf jeden Fall 2 Eingangsparameter:
x ... Die Zahl für die der Sinus zu berechnen ist
n ... Die Anzahl der Glieder der Summe die zu berücksichtigen ist.

Nun: Fur n gleich 1, ist diese Funktion einfach zu berechnen. Laut
Formel ist das Ergebnis dann 1
Für n grösser 1, wird zunächst der Funktionswert für n-1
Glieder berechnet, und dies dann mit dem Bruch

     2*(n-1) + 1
   ----------------
    (2*(n-1) + 1) !

entweder addiert oder subtrahiert (Wovon hängt das ab? Du
brauchst wieder einen Zusammenhang mit n).

sin(x,1) ->  1
sin(x,n) -> n gerade :
                              2*(n-1) + 1
              sin(x,n-1) -  ----------------
                            (2*(n-1) + 1) !

            n ungerade :
                              2*(n-1) + 1
              sin(x,n-1) +  ----------------
                            (2*(n-1) + 1) !


Implementiere das mal und dann überlegtst du dir wie
du die Berechnung vereinfachen kannst. Mit zusätzlichen
Funktionsparametern kann man nämlich das Berechnen des
Bruches auch wesentlich vereinfachen. Aus der Kenntnis
des Bruchwertes für n-1 kann man ganz leicht den Wert
des Bruches für n berechnen (aber das hast du schon
geschnallt, wie dein anderes Pgm beweist).
Hinweis: Du kannst im letzten Fall nicht nur mit dem
Returnwert der Funktion arbeiten, sondern du brauchst
aus deiner Funktion auch noch einen Ausgabeparameter,
der in einer lokalen Variablen aufgefangen wird.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um dir klar zu machen, wie diese Funktion ...

> int fac( int n )
> {
>   if( n == 0 )
>     return 1;
>   else
>     return n * fac( n-1 );
> }

mach mal folgende Abwandlung:

#include "stdio.h"

int fac( int n )
{
  int tmp;

  printf( "Fakultät von %d\n", n );

  if( n == 0 ) {
    printf( "** Einfach, das ist 1\n" );
    return 1;
  }
  else {
    printf( "** Das ist %d * fac( %d )\n", n, n-1);
    tmp = n * fac( n - 1 );
    printf( "** Daher ist %d * fac( %d ) gleich %d\n", n, n-1, res );
    return tmp;
}

int main()
{
  int n = 4;
  int res;

  printf( "fac( %d ) = ?\n", n );
  res = fac( n );
  printf( "fac( %d ) = %d\n", n, res );

  return 0;
}

Ich hab nichts anderes gemacht, als die ein paar Ausgaben
in die Funktion eingefügt. Das solltest du im übrigen auch
machen, wenn du dich an deiner Funktion versuchst.


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>   printf( "** Daher ist %d * fac( %d ) gleich %d\n", n, n-1, res );
                                                               ***

    printf( "** Daher ist %d * fac( %d ) gleich %d\n", n, n-1, tmp );

Sorry. Hab alles direkt hier eingetippt.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch ein Tippfehler:

> Nun: Fur n gleich 1, ist diese Funktion einfach zu berechnen. Laut
> Formel ist das Ergebnis dann 1

Ist natürlich Unsinn. Laut Formel ist das x.

> Für n grösser 1, wird zunächst der Funktionswert für n-1
> Glieder berechnet, und dies dann mit dem Bruch
>
>     2*(n-1) + 1
>   ----------------
>    (2*(n-1) + 1) !

Auch das ist natürlich Unsinn. Die 2*(n-1) + 1 im Zähler ergeben
den Exponenten, mit dem x zu potenzieren ist.

Tschuldigung dafür: Es ist leicht in diesem Eingabefeld den
Überblick zu verlieren. Vor allem wenn man versucht auch
noch eine einigermassen ansprechende Formatierung zu kriegen :-)


Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.