Funktionen und eine Einführung in Rekursion - fbi.h

Werbung
1
5
Funktionen
und eine Einführung
in Rekursion
 2006 Pearson Education, Inc. All rights reserved.
2
5.1
Einführung
5.2
Programmkomponenten in C
5.3
Funktionen der math.h Bibliothek
5.4
Funktionsdefinitionen mit mehreren Parametern
5.5
Funktionsprototypen und Argumentkonvertierung
5.6
Headerdateien der C Standardbibliothek
5.7
Fallstudie: Erzeugung von Zufallszahlen
5.8
Fallstudie: Ein Glücksspiel und Einführung von enum
5.9
Statische Speicherklasse
5.10
Regeln für Gültigkeitsbereiche
5.11
Rekursion
5.12
Fallstudie: Fibonacci-Reihe
5.13
Fallstudie: Türme von Hanoi
5.14
Rekursion und Iteration
 2006 Pearson Education, Inc. All rights reserved.
3
5.1 Einführung
• Technik des ‘Teile und Herrsche’
(Divide and Conquer)
– Konstruktion eines großen Programms aus kleinen,
einfachen Teilen (Komponenten)
• Funktionen
– Erleichtern Design, Implementierung, Betrieb und
Wartung von großen Programmen
 2006 Pearson Education, Inc. All rights reserved.
4
5.2 Programmkomponenten in C
• Funktionen
– Erlauben dem Programmierer ein Programm zu
modularisieren, indem einzelne Teilaufgaben in
selbstständigen Einheiten erledigt werden
• Die Anweisungen innerhalb des Funktionskörpers werden
nur einmal geschrieben.
– Können aber an mehreren Stellen im Programm
verwendet werden
– Vermeiden die Wiederholung von Code
– Werden vor anderen Funktionen verborgen
• Können auch in anderen Programmen wiederverwendet
werden
– Selbstdefinierte oder vordefinierte Bibliotheksfunktionen
 2006 Pearson Education, Inc. All rights reserved.
5
Betrachtung zum Software Engineering
Um die Wiederverwendung von Software zu
fördern, sollte jede Funktion auf eine einfache,
wohldefinierte Aufgabe beschränkt sein. Der
Name der Funktion sollte diese Aufgabe auf
zweckmäßige Art ausdrücken.
Solche Funktionen erleichtern es, Programme zu
schreiben, zu testen, zu debuggen und zu warten.
 2006 Pearson Education, Inc. All rights reserved.
6
Tipp zur Fehlervermeidung
Eine kleine Funktion, die eine einzige Aufgabe
erledigt, ist leichter zu testen und zu debuggen
als eine umfangreiche Funktion, die viele
Aufgaben ausführt.
 2006 Pearson Education, Inc. All rights reserved.
7
5.2 Programmkomponenten in C
• C Standardbibliothek
– Reichhaltige Sammlung von Funktionen für die
Ausführung üblicher Aufgaben, wie z.B.:
•
•
•
•
Mathematische Berechnungen
Manipulation von Zeichenketten
Manipulation von Zeichen
Ein- und Ausgabe
– Werden als Teil der C-Programmierumgebung zur
Verfügung gestellt
 2006 Pearson Education, Inc. All rights reserved.
8
5.3 Funktionen der math.h Bibliothek
• Funktionen
– Haben Funktionsprototypen, die üblicherweise in
Headerdateien stehen
• Können in jedem Programm wiederverwendet werden, das
die Headerdatei inkludiert und das den Objektcode der
Funktion mit einbindet
– Beispiel: sqrt in der <math.h> Headerdatei
• sqrt(900.0) berechnet die Quadratwurzel aus 900.0
• double sqrt(double) ist der Funktionsprototyp
 2006 Pearson Education, Inc. All rights reserved.
9
5.3 Funktionen der math.h Bibliothek
• Funktionsaufruf
– Eine Funktion wird durch einen Funktionsaufruf gestartet.
• Die aufgerufene Funktion gibt entweder ein Resultat zurück
oder sie gibt an ihrem Ende nach Ausführung der
gewünschten Anweisungen einfach die Kontrolle an die
aufrufende Funktion zurück (‘Funktion mit Seiteneffekten’).
• Funktionsparameter
– Information, die einer Funktion beim Funktionsaufruf vom
aufrufenden Programmteil übergeben wird
• sqrt(x) hat einen Parameter x, an den beim Funktionsaufruf
sqrt(900.0) das Funktionsargument 900.0 als Kopie
übergeben wird.
 2006 Pearson Education, Inc. All rights reserved.
10
Funktion
Beschreibung
Beispiel
ceil( x )
rundet x auf die kleinste ganze Zahl,
die nicht kleiner als x ist
ceil( 9.2 ) ist 10.0
ceil( -9.8 ) ist -9.0
cos( x )
Kosinus von x
(x in Radiant)
cos( 0.0 ) ist 1.0
exp( x )
Exponentialfunktion ex
exp( 1.0 ) ist 2.71828
exp( 2.0 ) ist 7.38906
fabs( x )
Absolutbetrag von x
fabs( 5.1 ) ist 5.1
fabs( 0.0 ) ist 0.0
fabs( -8.76 ) ist 8.76
floor( x )
rundet x auf die größte ganze Zahl,
die nicht größer als x ist
floor( 9.2 ) ist 9.0
floor( -9.8 ) ist -10.0
fmod( x, y )
Rest von x/y als Gleitpunktzahl
fmod( 2.6, 1.2 ) ist 0.2
log( x )
natürlicher Logarithmus von x
(Basis e)
log( 2.718282 ) ist 1.0
log( 7.389056 ) ist 2.0
log10( x )
Logarithmus von x (Basis 10)
log10( 10.0 ) ist 1.0
log10( 100.0 ) ist 2.0
pow( x, y )
x hoch y (xy)
pow( 2, 7 ) ist 128
pow( 9, .5 ) ist 3
sin( x )
Sinus x
(x in Radiant)
sin( 0.0 ) ist 0
sqrt( x )
Quadratwurzel von x (wobei x ein
nicht-negativer Wert ist)
sqrt( 9.0 ) ist 3.0
tan( x )
Tangens von x
(x in Radiant)
tan( 0.0 ) ist 0
Fig. 5.2 | Funktionen der math.h Bibliothek.
 2006 Pearson Education, Inc. All rights reserved.
5.4 Funktionsdefinitionen mit mehreren
Parametern
11
• Mehrere Parameter
– Funktionen erfordern oft mehr als eine einzige
Information, um ihre Aufgabe zu erfüllen.
– Entsprechend kann eine Funktion auch mehrere
Argumente vom aufrufenden Programmteil übernehmen.
– Die zugehörigen Parameter werden im Funktionsprototyp
und im Funktionskopf der Definition als eine
kommaseparierte Liste angegeben.
 2006 Pearson Education, Inc. All rights reserved.
5.4 Funktionsdefinitionen mit mehreren
Parametern
12
• Beispiel: Maximum von drei Zahlen
– Der Nutzer soll die von drei Studenten erreichten Punkte
als ganze Zahlen zwischen 0 und 100 eingeben.
– Das Programm soll das Maximum der drei Werte
bestimmen und ausgeben.
– Die Bestimmung des Maximums dreier ganzer Zahlen
wird als Funktion mit drei Parametern realisiert, die aus
main heraus aufgerufen wird.
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.5: fig05_05.c
2
// Prototype, use and definition of function maximum with three parameters.
3
#include <stdio.h>
Outline
13
4
5
int maximum( int x, int y, int z ); // function prototype
6
7
int main( void )
8
{
9
fig05_05.c
(1 von 2)
int grade1, grade2, grade3;
10
11
12
printf( "%s", "Enter three integer grades: " );
scanf( "%d%d%d", &grade1, &grade2, &grade3 );
13
Aufruf der Funktion maximum
übergibt drei Argumente
14
// output maximum of grades entered:
15
printf( "Maximum is: %d\n", maximum( grade1, grade2, grade3 ) );
16 } // end main
17
18 // function returns the maximum of its three integer parameters
19 int maximum( int x, int y, int z )
Kopf der Funktion maximum
20 {
21
int max = x; // assume x is the largest to start
22
// determine whether y is greater than max
23
24
if( y > max )
max = y; // make y the new max
25
// determine whether z is greater than max
26
if( z > max )
27
28
Kommaseparierte Parameterliste
max = z; // make z the new max
return max;
29 } // end function maximum
Rückgabe eines Wertes an den Aufrufer
 2006 Pearson Education,
Inc. All rights reserved.
Enter three integer grades: 86 67 75
Maximum is: 86
Enter three integer grades: 67 86 75
Maximum is: 86
Outline
14
fig05_05.c
(2 von 2)
Enter three integer grades: 67 75 86
Maximum is: 86
 2006 Pearson Education,
Inc. All rights reserved.
15
Häufiger Programmierfehler
Deklariert man Parameter des gleichen Typs
double x, y
als:
anstelle von :
double x, double y
so ist dies ein Syntaxfehler.
Die explizite Angabe eines Typs ist für jeden
Parameter in der Parameterliste erforderlich.
 2006 Pearson Education, Inc. All rights reserved.
16
Häufiger Programmierfehler
Falls Funktionsprototyp, Funktionskopf und
Funktionsaufruf nicht alle in bezug auf
Anzahl, Typ und Reihenfolge der Argumente
bzw. Parameter und im Rückgabetyp
übereinstimmen, führt dies zu einer
Fehlermeldung des Compilers.
 2006 Pearson Education, Inc. All rights reserved.
17
Betrachtung zum Software Engineering
Eine Funktion, die viele Parameter hat,
erledigt möglicherweise zu viele Aufgaben.
Die Funktion könnte in kleinere Funktionen
zerlegt werden, die die einzelnen Aufgaben
erledigen.
Der Funktionskopf sollte nicht länger als
eine Zeile sein.
 2006 Pearson Education, Inc. All rights reserved.
5.5 Funktionsprototypen und
Argumentkonvertierung
18
• Funktionsprototyp
– Auch als Funktionsdeklaration bezeichnet
– Gibt dem Compiler folgende Informationen:
• Name der Funktion
• Datentyp, den die Funktion zurückgibt
• Parameter, die die Funktion übernimmt:
– Anzahl der Parameter
– Typen der Parameter
– Reihenfolge der Parameter
 2006 Pearson Education, Inc. All rights reserved.
19
Betrachtung zum Software Engineering
Funktionsprototypen sind in C erforderlich.
Um die Funktionsprototypen der Funktionen der
C-Standardbibliothek zu erhalten, werden
#include Präprozessordirektiven benutzt, die
die Headerdateien der entsprechenden Bibliotheken einbinden (so ist z.B. der Prototyp der
Funktion sqrt im Header <math.h> enthalten).
Entsprechend kann #include zum Einbinden
eigener Headerdateien mit Funktionsprototypen
verwendet werden.
 2006 Pearson Education, Inc. All rights reserved.
20
Häufiger Programmierfehler
Falls eine Funktion vor ihrem ersten Aufruf
definiert wird, dient der Funktionskopf dieser
Definition auch als Funktionsprototyp, so dass ein
separater Prototyp unnötig ist.
Falls eine Funktion aufgerufen wird, bevor sie
definiert wird und dem Compiler zu diesem
Zeitpunkt kein Funktionsprototyp bekannt ist,
führt dies zu einer Fehlermeldung des Compilers.
 2006 Pearson Education, Inc. All rights reserved.
21
Betrachtung zum Software Engineering
Es sollten immer Funktionsprototypen zur
Verfügung gestellt werden, auch wenn es
möglich ist sie wegzulassen.
Sind Prototypen vorhanden, ist der Code
unabhängig von der Reihenfolge, in der
die Funktionen definiert werden, und
somit stabiler und besser wartbar.
 2006 Pearson Education, Inc. All rights reserved.
5.5 Funktionsprototypen und
Argumentkonvertierung
22
• C Konvertierungsregeln
– Geben an, wie verschiedene Typen ohne Informationsverlust ineinander umgewandelt werden können
– Werden auf Ausdrücke, die Werte mit zwei oder mehr
unterschiedlichen Datentypen enthalten, angewendet
• Solche Ausdrücke werden auch als Ausdrücke mit
Mischtypen (mixed-type expressions) bezeichnet.
• Jeder Wert des Ausdrucks wird in den “höchsten” Typ, der
in dem Ausdruck vorkommt, umgewandelt.
– Eine temporäre Version von jedem Wert wird erzeugt
und für den Ausdruck verwendet.
• Die Originalwerte bleiben unverändert.
 2006 Pearson Education, Inc. All rights reserved.
5.5 Funktionsprototypen und
Argumentkonvertierung
23
• C Konvertierungsregeln
– Eine Konvertierung findet auch statt, wenn der Typ eines
Funktionsarguments nicht mit dem festgelegten Parametertyp
übereinstimmt.
• Die Konvertierung wird so ausgeführt, als ob der Argumentwert
direkt der Parametervariablen zugewiesen würde.
• Beispielsweise kann eine Funktion mit einem ganzzahligen
Argument aufgerufen werden, auch wenn der Funktionsprototyp einen double-Wert als Parameter festlegt.
– Die Funktion wird trotzdem korrekt arbeiten.
 2006 Pearson Education, Inc. All rights reserved.
5.5 Funktionsprototypen und
Argumentkonvertierung
24
• C Konvertierungsregeln
– Konvertierung eines Wertes in einen ‘niedrigeren’
fundamentalen Typ:
• Führt normalerweise zu Informationsverlust oder falschem Wert
• Kann nur explizit durchgeführt werden
– Indem der Wert an eine Variable des niedrigeren Typs
zugewiesen wird (oft Compilerwarnung) oder
– Indem ein Konvertierungsoperator (‘cast’) benutzt wird
 2006 Pearson Education, Inc. All rights reserved.
25
Datentypen
long double
double
float
unsigned long int
(Synonym mit unsigned long)
long int
(Synonym mit long)
unsigned int
int
(Synonym mit unsigned)
unsigned short int
(Synonym mit unsigned short)
short int
unsigned char
char
bool
(Synonym mit short)
Fig. 5.6 | Konvertierungshierarchie für fundamentale Datentypen.
 2006 Pearson Education, Inc. All rights reserved.
26
5.6 Headerdateien der C-Standardbibliothek
• Headerdateien der C-Standardbibliothek
– Jeder Header enthält
• Funktionsprototypen der zugehörigen Funktionen
• Definitionen verschiedener Datentypen
• Konstanten, die von diesen Typen und Funktionen
benötigt werden
– Dem Compiler wird auf diese Weise mitgeteilt, wie
die Schnittstellen für Bibliotheks- (oder auch
selbstgeschriebene) Komponenten aussehen.
– Alle C-Bibliotheks-Headerdateien sind in Winkelklammern < > eingeschlossen und enden mit .h
 2006 Pearson Education, Inc. All rights reserved.
27
Headerdatei
Erläuterung
<stdio.h>
Enthält die Funktionsprototypen für die Ein- und Ausgabefunktionen des C-Standards und von diesen verwendete Info.
<stdlib.h>
Enthält die Funktionsprototypen für Konvertierungen von
Zahlen zu Text, Text zu Zahlen, Speicherallokation,
Zufallszahlen und verschiedene andere Hilfsfunktionen.
<math.h>
Enthält die Funktionsprototypen für Funktionen der
mathematischen Bibliothek.
<string.h>
Enthält die Funktionsprototypen für die Verarbeitung von
Zeichenketten.
Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 1 von 3).
 2006 Pearson Education, Inc. All rights reserved.
28
Headerdatei
Erläuterung
<time.h>
Enthält die Funktionsprototypen und Klassentypen für die
Manipulation von Zeit und Datum.
Enthält die Funktionsprototypen für Funktionen, die Zeichen in bezug
auf bestimmte Eigenschaften (z.B. ob das Zeichen eine Ziffer oder ein
Buchstabe ist) testen und Funktionsprototypen für Funktionen, die
zur Umwandlung von Kleinbuchstaben in Großbuchstaben und
umgekehrt verwendet werden.
Enthält Funktionsprototypen und Makros, um verschiedene
Umstände, die bei der Programmausführung auftreten können, zu
behandeln.
Definiert Makros zur Mitteilung von Fehlerzuständen.
<ctype.h>
<signal.h>
<errno.h>
Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 2 von 3).
 2006 Pearson Education, Inc. All rights reserved.
29
Headerdatei
Erläuterung
<assert.h>
Enthält Makros, die Diagnosehilfsmittel zum Debuggen eines
Programms zur Verfügung stellen.
<float.h>
Enthält die Grenzen der Gleitpunktgrößen für das jeweilige System.
<limits.h>
Enthält die Grenzen der ganzzahligen Größen für das jeweilige
System.
Enthält Funktionsprototypen und weitere Informationen, die benötigt
werden, um Daten in passender Form für unterschiedliche Sprachen
zu verarbeiten (z.B. Währungsformate, Zeichendarstellung,
Sortierung von Zeichenketten usw).
Enthält Funktionsprototypen für Funktionen, die es erlauben, den
normalen Funktionsaufrufmechanismus zu umgehen.
Definiert Makros für die Behandlung einer Liste von Funktionsargumenten mit unbekannter Anzahl und Typ.
Enthält allgemeine Definitionen von Typen, die C zur Durchführung
bestimmter Berechnungen verwendet.
<locale.h>
<setjmp.h>
<stdarg.h>
<stddef.h>
Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 3 von 3).
 2006 Pearson Education, Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
30
• Die Funktion rand der C-Standardbibliothek
– Ermöglicht die Nutzung von Zufallszahlen
in Rechneranwendungen
– Beispiel
rand();
– Erzeugt eine (pseudo)zufällige ganze Zahl vom Typ
unsigned int zwischen 0 und RAND_MAX (symbolische
Konstante, die im Header <stdlib.h> definiert wird)
– Der Funktionsprototyp für die rand Funktion befindet sich
ebenfalls in <stdlib.h>
 2006 Pearson Education, Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
31
• Um zufällige ganze Zahlen in einem bestimmten Bereich
zu erhalten, kann der modulo-Operator (%) zusammen mit
rand benutzt werden.
– Beispiel
rand() % 6;
– Erzeugt ganze Zahlen im Bereich von 0 bis 5
– Man spricht von Skalierung; 6 ist der Skalierungsfaktor.
– Durch zusätzliche Verschiebung entsteht der Bereich von 1 bis 6:
1 + rand() % 6;
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.8: fig05_08.c
2
// Shifted and scaled random integers.
3
#include <stdio.h>
4
#include <stdlib.h> // contains function prototype for rand
5
6
int main( void )
7
{
Outline
#include für die rand Funktion
unsigned int counter;
9
// loop 20 times
10
for( counter = 1; counter <= 20; ++counter ) {
11
// pick random number from 1 to 6 and output it
12
printf( "%10u", 1 + rand() % 6 );
13
// if counter is divisible by 5, start a new line of output
14
if( counter % 5 == 0 )
16
fig05_08.c
(1 von 1)
8
16
32
puts( "" );
Aufruf der rand Funktion
} // end for
17 } // end main
6
5
6
6
6
1
6
2
5
1
2
3
5
5
4
4
6
3
2
1
 2006 Pearson Education,
Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
33
• Beispiel:
– Simulation, bei der ein fairer Würfel 6 Millionen mal geworfen
wird
– Ermitteln der entstehenden Häufigkeitsverteilung:
• Jedes einzelne Auftreten einer der Zahlen von 1 bis 6 wird in einer
eigenen Variablen, die über eine switch-Struktur angesprochen
wird, gezählt.
• Diese Strategie zur Ermittlung einer Häufigkeitsverteilung ist
umständlich und wird in Kapitel 6 verbessert.
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.9: fig05_09.c
2
// Roll a six-sided die 6,000,000 times.
3
4
#include <stdio.h>
5
6
#include <stdlib.h> // contains function prototype for rand
7
int main( void )
8
{
Outline
34
fig05_09.c
9
unsigned int frequency1 = 0; // count of 1s rolled
10
unsigned int frequency2 = 0; // count of 2s rolled
11
12
unsigned int frequency3 = 0; // count of 3s rolled
unsigned int frequency4 = 0; // count of 4s rolled
13
unsigned int frequency5 = 0; // count of 5s rolled
14
unsigned int frequency6 = 0; // count of 6s rolled
(1 von 3)
15
16
unsigned int roll; // roll counter, value 1 to 6,000,000
17
unsigned int face; // one roll of the die, value 1 to 6
18
19
// loop 6,000,000 times and summarize results
20
for( roll = 1; roll <= 6000000; ++roll ) {
21
face = 1 + rand() % 6; // random number from 1 to 6
Skalierung und Verschiebung
des von rand erzeugten Wertes
 2006 Pearson Education,
Inc. All rights reserved.
22
23
// determine face value and increment appropriate counter
24
switch( face ) {
25
26
27
28
case 1:
++frequency1; // increment the 1s counter
break;
case 2:
29
++frequency2; // increment the 2s counter
30
break;
31
32
33
34
case 4:
36
break;
40
case 5:
++frequency5; // increment the 5s counter
break;
case 6:
41
++frequency6; // increment the 6s counter
42
break;
43
44
45
46
(2 von 3)
++frequency3; // increment the 3s counter
break;
++frequency4; // increment the 4s counter
38
39
fig05_09.c
case 3:
35
37
Outline
35
default: // invalid value
puts( "Program should never get here!" );
} // end switch
} // end for
 2006 Pearson Education,
Inc. All rights reserved.
47
48
printf( "%s%13s\n", "Face", "Frequency" ); // output header
49
printf( "
1%13u\n", frequency1 );
50
printf( "
2%13u\n", frequency2 );
51
printf( "
3%13u\n", frequency3 );
52
printf( "
4%13u\n", frequency4 );
53
printf( "
5%13u\n", frequency5 );
54
printf( "
6%13u\n", frequency6 );
55 } // end main
Face
1
2
3
4
5
6
Frequency
999702
1000823
999378
998898
1000777
1000422
Outline
36
fig05_09.c
(3 von 3)
Jede Augenzahl des Würfels erscheint etwa 1.000.000 mal
 2006 Pearson Education,
Inc. All rights reserved.
37
Tipp zur Fehlervermeidung
In einem switch sollte immer ein default Case
vorhanden sein, der auf Fehlerzustände hinweist –
auch wenn man absolut sicher ist, dass keine
Fehler auftreten können!
 2006 Pearson Education, Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
38
• Funktion rand
– Erzeugt pseudozufällige Zahlen
– Bei jeder Programmausführung wird die gleiche, fest
vorgegebene Zahlenfolge erzeugt.
• Randomisierung
– Ein Programm wird dazu gebracht, bei jeder Ausführung
unterschiedliche Zufallszahlenfolgen zu erzeugen.
• Die Funktion srand der C-Standardbibliothek
– Übernimmt ein Argument vom Typ unsigned int
– Legt in Abhängigkeit von ihrem Argument einen Einstiegspunkt in die (feste) Zahlenfolge, die rand erzeugt, fest.
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.10: fig05_10.c
2
// Randomizing die-rolling program.
3
#include <stdio.h>
4
#include <stdlib.h> // contains prototypes for functions srand and rand
Outline
39
5
6
int main( void )
7
{
unsigned ist eine Abkürzung für unsigned int
(1 von 2)
8
unsigned counter;
9
unsigned seed; // number used to seed random number generator
10
fig05_10.c
seed wird an srand übergeben, um
die benutzte Zufallsfolge festzulegen
11
printf( "%s", "Enter seed: " );
12
scanf( "%u", &seed );
13
srand( seed ); // seed random number generator
14
15
// loop 10 times
16
for( counter = 1; counter <= 10; ++counter ) {
17
// pick random number from 1 to 6 and output it
18
printf( "%10u", 1 + rand() % 6 );
19
// if counter is divisible by 5, start a new line of output
20
if( counter % 5 == 0 )
21
22
puts( "" );
} // end for
23 } // end main
 2006 Pearson Education,
Inc. All rights reserved.
Enter seed: 67
6
1
1
6
4
1
6
6
2
4
Enter seed: 432
4
3
6
1
3
5
1
4
6
2
Outline
40
fig05_10.c
(2 von 2)
Enter seed: 67
6
1
1
6
4
1
6
6
2
4
Nur unterschiedliche Werte von seed
führen zu unterschiedlichen Zufallsfolgen
 2006 Pearson Education,
Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
41
• Randomisierung, ohne jedes Mal einen anderen
Wert von seed einzugeben:
srand( time( NULL ) );
• Der Rechner liest seine interne Uhr, um einen Wert für
seed zu erhalten.
– Funktion time (mit dem Argument NULL)
• Gibt die aktuelle Zeit als Anzahl der Sekunden zurück, die
seit dem 1. Januar 1970, 0 Uhr (Greenwich Mean Time,
GMT) vergangen sind.
• Der Funktionsprototyp für time ist in <time.h> enthalten.
 2006 Pearson Education, Inc. All rights reserved.
42
Häufiger Programmierfehler
Wird die Funktion srand( time( NULL ) )
mehr als einmal in einem Programm aufgerufen,
wird die Pseudozufallszahlenfolge wieder neu
gestartet (wenn weniger als eine Sekunde
vergangen ist, wieder an der gleichen Stelle wie
zuvor!).
In diesem Fall sind die von rand erzeugten
Zahlen nicht mehr zufällig.
 2006 Pearson Education, Inc. All rights reserved.
5.7 Fallstudie:
Erzeugung von Zufallszahlen
43
• Skalierung und Verschiebung von Zufallszahlen
– Um Zufallszahlen mit einem gewünschten Wertebereich zu
erzeugen, kann folgende Anweisung benutzt werden:
number = Verschiebungswert +
rand() % Skalierungsfaktor;
• Verschiebungswert entspricht dem ersten Wert im gewünschten Bereich von aufeinanderfolgenden ganzen Zahlen.
• Skalierungsfaktor entspricht der Breite des gewünschten
Wertebereiches von aufeinanderfolgenden ganzen Zahlen.
– Dies ist die Anzahl von aufeinanderfolgenden ganzen
Zahlen im gewünschten Wertebereich.
 2006 Pearson Education, Inc. All rights reserved.
5.8 Fallstudie: Ein Glücksspiel und
Einführung von enum
44
• Enumeration
– Eine Menge von ganzzahligen Konstanten, die durch
Bezeichner dargestellt werden
• Die Werte der Enumerationskonstanten beginnen mit 0 – es sei denn,
ein anderer ganzzahliger Startwert wird angegeben - und werden
jeweils um 1 inkrementiert.
• Die Bezeichner in einer Enumeration müssen eindeutig sein, aber
unterschiedliche Enumerationskonstanten können den gleichen
ganzzahligen Wert haben.
– Definition einer Enumeration
• Schlüsselwort enum
• Erzeugt eine Typbezeichnung
• Kommaseparierte Liste von Bezeichnern innerhalb geschweifter
Klammern
• Beispiel
enum Months { JAN = 1, FEB, MAR, APR };
 2006 Pearson Education, Inc. All rights reserved.
5.8 Fallstudie: Ein Glücksspiel und
Einführung von enum
45
• Regeln für das Würfelspiel Craps
– Der Spieler spielt gegen die Bank.
– Der Spieler wirft zwei Würfel.
• 7 oder 11 beim ersten Wurf: Der Spieler gewinnt.
• 2, 3 oder 12 beim ersten Wurf: Der Spieler verliert.
• 4, 5, 6, 8, 9, 10 beim ersten Wurf:
– Die geworfene Augenzahl wird zum "point“ des
Spielers.
– Um zu gewinnen, muss der Spieler seinen point noch
einmal werfen, bevor er eine 7 wirft.
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.11: fig05_11.c
2
// Craps simulation.
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <time.h> // contains prototype for function time
Outline
46
fig05_11.c
6
7
// enumeration constants that represent the game status
8
enum Status { CONTINUE, WON, LOST }; // all caps in constants
(1 von 4)
9
10 unsigned rollDice( void ); // function prototype
Enumeration
zur Darstellung des Spielzustands
11
12 int main( void )
13 {
14
unsigned sumOfDice; // sum of rolled dice
15
unsigned myPoint; // point if no win or loss on first roll
16
enum Status gameStatus; // can contain CONTINUE, WON or LOST
17
18
// randomize random number generator using current time
19
srand( time( NULL ) );
Definition einer Variablen des
Enumerationstyps Status
20
21
22
sumOfDice = rollDice(); // first roll of the dice
Setzen des Zufallszahlengenerators mit der aktuellen Zeit
 2006 Pearson Education,
Inc. All rights reserved.
23
// determine game status and point (if needed) based on first roll
24
switch( sumOfDice ) {
25
case 7: // win with 7 on first roll
26
case 11: // win with 11 on first roll
27
gameStatus = WON;
28
break;
29
case 2: // lose with 2 on first roll
30
case 3: // lose with 3 on first roll
31
case 12: // lose with 12 on first roll
32
gameStatus = LOST;
33
break;
34
Outline
Zuweisung einer Enumerationskonstanten
an gameStatus
fig05_11.c
(2 von 4)
default: // did not win or lose, so remember point
35
gameStatus = CONTINUE; // game is not over
36
myPoint = sumOfDice; // remember the point
37
printf( "Point is %u\n", myPoint );
38
break; // optional at end of switch
39
} // end switch
40
Vergleich einer Variablen des Enumerationstyps
mit einer Enumerationskonstanten
41
// while game is not complete
42
while( gameStatus == CONTINUE ) { // not WON or LOST
43
47
sumOfDice = rollDice(); // roll dice again
44
45
// determine game status
46
if( sumOfDice == myPoint ) // win by making point
47
48
49
50
51
gameStatus = WON;
else
if( sumOfDice == 7 ) // lose by rolling 7 before point
gameStatus = LOST;
} // end while
 2006 Pearson Education,
Inc. All rights reserved.
52
53
// display won or lost message
54
if( gameStatus == WON )
55
56
57
Outline
48
puts( "Player wins" );
else
puts( "Player loses" );
fig05_11.c
58 } // end main
59
(3 von 4)
60 // roll dice, calculate sum and display results
61 unsigned rollDice( void )
62 {
63
unsigned die1; // first die
64
unsigned die2; // second die
65
unsigned sum; // sum of dice
Funktion, die die Aufgabe des
Werfens zweier Würfel erledigt
66
67
// pick random die values
68
die1 = 1 + rand() % 6; // first die roll
69
die2 = 1 + rand() % 6; // second die roll
70
sum = die1 + die2; // compute sum of die values
71
72
// display results of this roll
73
printf( "Player rolled %u + %u = %u\n", die1, die2, sum );
74
return sum; // end function rollDice
75 } // end function rollDice
 2006 Pearson Education,
Inc. All rights reserved.
Player rolled 2 + 5 = 7
Player wins
Player rolled 6 + 6 = 12
Player loses
Player rolled
Point is 6
Player rolled
Player rolled
Player rolled
Player rolled
Player wins
3 + 3 = 6
Player rolled
Point is 4
Player rolled
Player rolled
Player rolled
Player rolled
Player rolled
Player rolled
Player rolled
Player rolled
Player loses
1 + 3 = 4
5
4
2
1
4
2
6
2
2
1
4
4
+
+
+
+
+
+
+
+
+
+
+
+
3
5
1
5
6
4
4
3
4
1
4
3
=
=
=
=
=
=
=
=
=
=
=
=
Outline
49
fig05_11.c
(4 von 4)
8
9
3
6
10
6
10
5
6
2
8
7
 2006 Pearson Education,
Inc. All rights reserved.
50
Praxis-Tipp
Bezeichner für einen nutzerdefinierten Typ
sollten mit Großbuchstaben beginnen.
Enumerationskonstanten sollten komplett mit
Großbuchstaben geschrieben werden.
Dadurch fallen diese Konstanten im Programm
besonders auf und können nicht mit Variablen
verwechselt werden.
 2006 Pearson Education, Inc. All rights reserved.
51
Praxis-Tipp
Der Einsatz von Enumerationen anstelle von
ganzzahligen Konstanten macht Programme
klarer und damit besser wartbar.
Der Wert einer Enumerationskonstanten wird
einmal in der Deklaration der Enumeration
gesetzt.
 2006 Pearson Education, Inc. All rights reserved.
52
Häufiger Programmierfehler
Versucht man das ganzzahlige Äquivalent einer
Enumerationskonstanten einer Variablen vom
Enumerationstyp zuzuweisen, führt dies zu einer
Fehlermeldung des Compilers.
Versucht man einer Enumerationskonstanten
einen anderen Wert zuzuweisen, nachdem diese
Konstante einmal definiert wurde, führt dies zu
einer Fehlermeldung des Compilers.
 2006 Pearson Education, Inc. All rights reserved.
53
5.9 Statische Speicherklasse
• Jeder Bezeichner hat
– Speicherklasse
• Legt die Zeitdauer fest, während der ein Bezeichner im
Speicher existiert
– Gültigkeitsbereich
• Legt fest, wo innerhalb eines Programms auf einen
Bezeichner zugegriffen werden kann
– dateiübergreifende Gültigkeit
• Legt fest, ob ein Bezeichner nur in der Quelldatei, in der er
deklariert wurde, bekannt ist oder in mehreren, einzeln
übersetzen Dateien, die dann gemeinsam gebunden werden.
 2006 Pearson Education, Inc. All rights reserved.
54
5.9 Statische Speicherklasse
• Statische Speicherklasse
– Variablen mit statischer Speicherklasse
• Existieren ab dem Punkt, an dem die Programmausführung beginnt
• Werden nur einmal an der Stelle ihrer Definition initialisiert
• Existieren bis zum Programmende
– Obwohl die Variablen vom Beginn der Programmausführung an
existieren, bedeutet dies nicht, dass sie während des gesamten
Programms benutzt werden können.
 2006 Pearson Education, Inc. All rights reserved.
55
5.9 Statische Speicherklasse
• Es gibt zwei Typen von Bezeichnern mit statischer
Speicherklasse
a) Externe Bezeichner
•
Globale Variablen (und globale Funktionsnamen)
b) Lokale Variablen, die mit dem Speicherklassenspezifizierer
static deklariert werden
 2006 Pearson Education, Inc. All rights reserved.
56
5.9 Statische Speicherklasse
• Globale Variablen
– Werden erzeugt, indem die Variablendeklaration außerhalb jeder
Klassen- oder Funktionsdefinition geschrieben wird
– Behalten ihre Werte während der gesamten Programmausführung
– Können aus allen Funktionen in der gleichen Quelldatei
angesprochen werden, die nach ihren Deklarationen stehen
 2006 Pearson Education, Inc. All rights reserved.
57
5.9 Statische Speicherklasse
• Lokale Variablen, die als static deklariert werden
– Sind nur innerhalb der Funktion bekannt, in der sie deklariert
werden
– Behalten ihre Werte, wenn die Funktion beendet wird und zu
ihrem Aufrufpunkt zurückkehrt
• Wenn die Funktion das nächste Mal aufgerufen wird, haben
die static lokalen Variablen die Werte, die sie hatten, als
die Funktion zuletzt beendet wurde.
– Falls nummerische Variablen mit statischer Speicherklasse nicht
explizit vom Programmierer initialisiert werden, werden sie
automatisch mit Null initialisiert.
 2006 Pearson Education, Inc. All rights reserved.
58
Betrachtung zum Software Engineering
Wird eine Variable als global statt als lokal
deklariert, können unbeabsichtigte Seiteneffekte
auftreten: Eine Funktion, die eigentlich keinen
Zugriff auf diese Variable benötigt, könnte diese
unbeabsichtigt oder in schlechter Absicht verändern.
Im allgemeinen sollten globale Variablen vermieden
werden außer in bestimmten Situationen mit
extremen Performanzanforderungen.
 2006 Pearson Education, Inc. All rights reserved.
59
Betrachtung zum Software Engineering
Dies ist ein Beispiel für das ‘Prinzip der
möglichst geringen Berechtigungen’
(principle of least privilege).
Dieses Prinzip sagt aus, dass Code nur das
Ausmaß an Berechtigungen und an
Zugriffserlaubnis bekommen darf, das für die
Erfüllung der konkreten Aufgabe nötig ist,
aber nicht mehr.
 2006 Pearson Education, Inc. All rights reserved.
60
Betrachtung zum Software Engineering
Variablen, die nur innerhalb einer bestimmten
Funktion benutzt werden, sollten als lokale
Variablen in dieser Funktion deklariert werden.
 2006 Pearson Education, Inc. All rights reserved.
61
5.10 Regeln für Gültigkeitsbereiche
• Gültigkeitsbereich (scope)
– Teil des Programms, wo ein Bezeichner verwendet werden
kann
• Wichtige Gültigkeitsbereiche für einen Bezeichner sind:
– Dateiscope
– Blockscope
 2006 Pearson Education, Inc. All rights reserved.
62
5.10 Regeln für Gültigkeitsbereiche
• Dateiscope
– Für einen Bezeichner, der außerhalb jeder Funktion oder
Klasse deklariert ist
• Ein solcher Bezeichner ist in allen Funktionen ab dem Punkt, an dem
er deklariert wurde, bis zum Dateiende bekannt.
– Globale Variablen, Funktionsdefinitionen und Funktionsprototypen außerhalb einer Funktion haben Dateiscope.
• Sollen sie auch in einer weiteren Datei verwendet werden, können sie
mit Hilfe des Schlüsselwortes extern dort deklariert werden.
 2006 Pearson Education, Inc. All rights reserved.
63
5.10 Regeln für Gültigkeitsbereiche
• Blockscope
– Bezeichner, die innerhalb eines Blocks deklariert werden, haben
Blockscope.
• Blockscope beginnt an der Deklaration des Bezeichners.
• Blockscope endet an der schließenden geschweiften Klammer (})
des Blocks, in dem der Bezeichner deklariert ist.
– Lokale Variablen und Funktionsparameter haben Blockscope.
• Der Funktionskörper ist ihr Block.
– Jeder Block kann Variablendeklarationen enthalten.
– Bezeichner in einem äußeren Block können durch einen lokalen
Bezeichner in einem geschachtelten inneren Block mit gleichem
Namen verborgen werden.
– Lokale static Variablen haben Blockscope, obwohl sie auch
außerhalb des Blocks weiter existieren.
• Lebensdauer und Gültigkeitsbereich sind unabhängig voneinander.
 2006 Pearson Education, Inc. All rights reserved.
64
Häufiger Programmierfehler
Der versehentliche Gebrauch des gleichen
Bezeichners für eine Variable in einem inneren
Block und für eine andere Variable in einem
äußeren Block ist normalerweise ein logischer
Fehler (kein Übersetzungsfehler!).
 2006 Pearson Education, Inc. All rights reserved.
65
Praxis-Tipp
Variablennamen, die Namen in umschließenden
Gültigkeitsbereichen verbergen, sollten
vermieden werden. Dies kann erreicht werden,
indem kein Bezeichner mehrfach in einem
Programm eingesetzt wird.
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.12: fig05_12.c
2
// A scoping example.
3
#include <stdio.h>
Outline
66
4
5
void useLocal( void ); // function prototype
6
void useStaticLocal( void ); // function prototype
7
void useGlobal( void ); // function prototype
8
9
int x = 1; // global variable
10
11 int main( void )
12 {
13
fig05_12.c
Deklaration einer globalen Variablen außerhalb
(1 von 4)
jeder Klassen- und Funktionsdefinition
Lokale Variable x, die die globale Variable x verbirgt
int x = 5; // local variable to main
14
15
printf( "local x in main's outer scope is %d\n", x );
16
17
18
{ // start new scope
int x = 7; // hides x in outer scope
Lokale Variable x in einem Block, die die lokale
Variable x im äußeren Gültigkeitsbereich verbirgt
19
20
21
printf( "local x in main's inner scope is %d\n", x );
} // end new scope
22
23
printf( "local x in main's outer scope is %d\n", x );
 2006 Pearson Education,
Inc. All rights reserved.
24
25
useLocal(); // useLocal has automatic local x
26
useStaticLocal(); // useStaticLocal has static local x
27
useGlobal(); // useGlobal uses global x
28
useLocal(); // useLocal reinitializes automatic local x
29
useStaticLocal(); // static local x retains its prior value
30
useGlobal(); // global x also retains its value
31
32
Outline
67
fig05_12.c
(2 von 4)
printf( "\nlocal x in main is %d\n", x );
33 } // end main
34
35 // useLocal reinitializes local
x during
Lokalevariable
Variable,
die bei each
jedemcall
Aufruf
36 void useLocal( void )
von
useLocal neu erzeugt und initialisiert wird
37 {
38
int x = 25; // initialized each time useLocal is called
39
40
printf( "\nlocal x is %d on entering useLocal\n", x );
41
++x;
42
printf( "local x is %d on exiting useLocal\n", x );
43 } // end function useLocal
 2006 Pearson Education,
Inc. All rights reserved.
44
45 // useStaticLocal initializes static local variable x only the
46 // first time the function is called; value of x is saved
Outline
68
47 // between calls to this function
48 void useStaticLocal( void )
static lokale Variable, die nur einmal initialisiert wird
fig05_12.c
49 {
50
static int x = 50; // initialized first time useStaticLocal is called
51
52
printf( "\nlocal static x is %d on entering useStaticLocal\n", x );
53
++x;
54
printf( "local static x is %d on exiting useStaticLocal\n", x );
(3 von 4)
55 } // end function useStaticLocal
56
57 // useGlobal modifies global variable x during each call
58 void useGlobal( void )
59 {
60
printf( "\nglobal x is %d on entering useGlobal\n", x );
61
x *= 10;
62
printf( "global x is %d on exiting useGlobal\n", x );
63 } // end function useGlobal
Die Anweisung gilt für die globale Variable
x, da hier keine lokale Variable x existiert
 2006 Pearson Education,
Inc. All rights reserved.
local x in main's outer scope is 5
local x in main's inner scope is 7
local x in main's outer scope is 5
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
Outline
69
fig05_12.c
(4 von 4)
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
 2006 Pearson Education,
Inc. All rights reserved.
70
5.11 Rekursion
• Rekursive Funktion
– Eine Funktion, die sich selbst aufruft, entweder direkt, oder
indirekt (durch eine andere Funktion)
• Rekursion kann als Wiederholungsstruktur eingesetzt werden
– Durch Aufteilung der Funktion in
• Basisfall (‘Base case’)
– Die einfachste Variante der Problemlösung, die direkt in die
Funktion hinein programmiert wird.
• und wiederholten rekursiven Aufruf von sich selbst.
 2006 Pearson Education, Inc. All rights reserved.
71
5.11 Rekursion
• Rekursiver Aufruf (Rekursionsschritt)
– Die Funktion teilt das zu lösende Problem in zwei
begriffliche Teile auf:
• Eine etwas einfachere oder kleinere Version des
ursprünglichen Problems
• Der Unterschied, der zwischen der einfacheren Version und
dem ursprünglichen Problem besteht
– Die Funktion startet (d.h. ruft auf) eine neue Kopie von
sich selbst, die die einfachere Version des Problems
bearbeiten soll.
 2006 Pearson Education, Inc. All rights reserved.
72
5.11 Rekursion
• Rekursiver Aufruf (Rekursionsschritt)
– Dies kann zu vielen weiteren rekursiven Aufrufen führen,
indem die Funktion weiterhin jede neu entstandene
Version in die beiden begrifflichen Teile zerlegt.
– Diese Folge von immer einfacheren Problemen muss
schließlich in den Basisfall münden.
• Falls nicht, würde sich die Rekursion unendlich fortsetzen.
 2006 Pearson Education, Inc. All rights reserved.
73
5.11 Rekursion
• Beispiel: Fakultät
– Die Fakultät einer nichtnegativen ganzen Zahl n,
geschrieben n! (und gesprochen “n Fakultät”), ist das
Produkt
n · (n – 1) · (n – 2) · … · 1
– Rekursive Definition der Funktion
n! = n · (n – 1)!
• Beispiel
5! = 5 · 4 · 3 · 2 · 1
5! = 5 · ( 4 · 3 · 2 · 1)
5! = 5 · ( 4! )
 2006 Pearson Education, Inc. All rights reserved.
74
Fig. 5.28 | Rekursive Entwicklung von 5!
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.29: fig05_29.c
2
// Recursive factorial function.
3
#include <stdio.h>
Outline
75
4
5
unsigned long long factorial( unsigned long ); // function prototype
fig05_29.c
6
7
int main( void )
8
{
9
(1 von 2)
unsigned int counter;
10
11
// calculate the factorials of 0 through 20 (try 21 also)
12
for( counter = 0; counter <= 21; ++counter )
13
printf( "%2u! = %llu\n", counter, factorial( counter ) );
14 } // end main
Erster Aufruf der factorial Funktion
15
16 // recursive definition of function factorial
17 unsigned long long factorial( unsigned long number )
18 {
19
20
21
22
if( number <= 1 ) // test for base case
Basisfall (base case):
einfach 1 zurückgeben
return 1; // base cases: 0! = 1 and 1! = 1
else // recursion step
return number * factorial( number - 1 );
23 } // end function factorial
Rekursiver Aufruf der factorial
Funktion für ein etwas einfacheres Problem
 2006 Pearson Education,
Inc. All rights reserved.
0!
1!
2!
3!
4!
5!
6!
7!
8!
9!
10!
11!
12!
13!
14!
15!
16!
17!
18!
19!
20!
21!
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000
2432902008176640000
14197454024290336768
Outline
76
fig05_29.c
(2 von 2)
 2006 Pearson Education,
Inc. All rights reserved.
77
5.11 Rekursion
• Beispiel: Fakultät
– Der Umwandlungsspezifizierer %llu wird für die Ausgabe von
unsigned long long int Werten benötigt.
– Die Funktion factorial produziert so schnell sehr große
Werte, dass auch unsigned long long int nur bis zum Wert
für n = 20 ausreicht, bevor der darstellbare Zahlenbereich
überschritten wird.
– Hier zeigt sich eine Schwäche von C — die Sprache ist nicht
leicht erweiterbar, um die Anforderungen unterschiedlicher
Anwendungen erfüllen zu können.
– Im zweiten Semester lernen wir mit C++ eine erweiterbare
Sprache kennen, die es erlaubt durch “Klassen” einfach neue
Datentypen zu erzeugen, die beispielsweise beliebig große ganze
Zahlen verarbeiten können, wenn wir dies benötigen.
 2006 Pearson Education, Inc. All rights reserved.
78
Häufiger Programmierfehler
Falls man den Basisfall vergisst oder den
Rekursionsschritt falsch realisiert, so dass er
nicht zum Basisfall konvergiert, entsteht eine
“unendliche” Rekursion, die schließlich den
verfügbaren Speicher erschöpft.
Dies ist vergleichbar mit einer Endlosschleife in
einer iterativen (d.h. nichtrekursiven) Lösung
des Problems.
 2006 Pearson Education, Inc. All rights reserved.
79
5.12 Fallstudie: Fibonacci-Reihe
• Die Fibonacci-Reihe
– 0, 1, 1, 2, 3, 5, 8, 13, 21, …
– Beginnt mit 0 und 1
– Jede folgende Fibonacci-Zahl ist die Summe der beiden
vorangehenden Fibonacci-Zahlen.
• Dies kann rekursiv definiert werden:
F(0) = 0
F(1) = 1
F(n) = F(n – 1) + F(n – 2)
 2006 Pearson Education, Inc. All rights reserved.
80
5.12 Fallstudie: Fibonacci-Reihe
• Erste Alternative zur Rekursion:
Iterative Lösung
– Initialisierung von zwei Variablen mit den beiden
Anfangswerten 0 und 1
– Verwendung einer Schleife, die
• den nächsten Wert als Summe aus den beiden
vorangegangenen Werten berechnet,
• die Variablen, die die beiden vorangegangenen Werte
enthalten, mit dem jeweils folgenden Wert der FibonacciReihe aktualisiert.
 2006 Pearson Education, Inc. All rights reserved.
81
5.12 Fallstudie: Fibonacci-Reihe
• Zweite Alternative zur Rekursion:
Analytische Lösung
– Differenzengleichung
F(n) – F(n-1) – F(n-2) = 0
• Mit den Anfangsbedingungen
F(0) = 0
F(1) = 1
– charakteristische Gleichung
z² - z -1 = 0
 2006 Pearson Education, Inc. All rights reserved.
82
5.12 Fallstudie: Fibonacci-Reihe
– Lösungen der charakteristischen Gleichung
z1, 2
1
5
 
2 2
– Allgemeine Lösung der Differenzengleichung
 


F(n) 
1 5
2
n
1 5
2
n
5
 2006 Pearson Education, Inc. All rights reserved.
1
// Fig. 5.30: fig05_30.c
2
3
4
5
6
7
// Recursive, iterative and analytical computation of Fibonacci series.
8
9
10
11
12
13
unsigned long long int fiboIterative( unsigned int ); // iterative computation
14
15
16
17
18
19
20
#include <stdio.h>
Outline
83
#include <time.h>
#include <math.h>
unsigned long long int fiboRecursive( unsigned int ); // recursive computation
unsigned long long int fiboAnalytical( unsigned int ); // mathematical analysis
fig05_30.c
(1 von 4)
int main( void )
{
unsigned int n, nStart, nEnd;
clock_t start;
printf( "%s", "Fibonacci series.\n\nEnter first value of n: " );
scanf( "%u", &nStart );
printf( "%s", "Enter last value of n: " );
scanf( "%u", &nEnd );
 2006 Pearson Education,
Inc. All rights reserved.
21
22
for( n = nStart; n <= nEnd; ++n ) {
23
start = clock();
24
printf( "\nRecursive:
25
printf( "
n = %u
Outline
84
F(n) = %10llu", n, fiboRecursive( n ) );
(%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC );
26
27
start = clock();
28
printf( "Iterative:
29
printf( "
fig05_30.c
n = %u
F(n) = %10llu", n, fiboIterative( n ) );
(%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC );
(2 von 4)
30
31
start = clock();
32
printf( "Analytical: n = %u
33
printf( "
34
F(n) = %10llu", n, fiboAnalytical( n ) );
(%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC );
}
35 } // end main
36
37 // recursive method
38 unsigned long long int fiboRecursive( unsigned int n )
39 {
40
41
42
43
if( ( n == 0 ) || ( n == 1 ) ) // base cases
return n;
else // recursion step
return fiboRecursive( n - 1 ) + fiboRecursive( n - 2 );
44 } // end function fiboRecursive
45
 2006 Pearson Education,
Inc. All rights reserved.
46 // iterative method
47 unsigned long long int fiboIterative( unsigned int n )
48 {
49
unsigned long long int previous, now, next;
50
unsigned int i;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
previous = 0 ; // F(0)
now = 1 ; // F(1)
next = n ; // result in cases n = 0 or n = 1
Outline
85
fig05_30.c
(3 von 4)
for( i = 2 ; i <= n; ++i ) {
next = now + previous;
previous = now;
now = next;
}
return next;
} // end function fiboIterative
// method using solution of difference equation
unsigned long long int fiboAnalytical( unsigned int nPar )
{
67
double GOLDEN_RATIO, n;
68
GOLDEN_RATIO = ( 1. + sqrt( 5. ) ) / 2.;
69
n = (double) nPar;
70
return ( pow( GOLDEN_RATIO, n ) – pow( 1. - GOLDEN_RATIO, n ) ) / sqrt( 5. );
71 } // end function fiboAnalytical
 2006 Pearson Education,
Inc. All rights reserved.
Fibonacci series.
Outline
86
Enter first value of n: 42
Enter last value of n: 46
Recursive: n = 42
Iterative: n = 42
Analytical: n = 42
F(n) =
F(n) =
F(n) =
267914296
267914296
267914296
(34.66 sec)
(0.00 sec)
(0.00 sec)
Recursive: n = 43
Iterative: n = 43
Analytical: n = 43
F(n) =
F(n) =
F(n) =
433494437
433494437
433494437
(56.05 sec)
(0.00 sec)
(0.00 sec)
Recursive: n = 44
Iterative: n = 44
Analytical: n = 44
F(n) =
F(n) =
F(n) =
701408733
701408733
701408733
(90.70 sec)
(0.00 sec)
(0.00 sec)
Recursive: n = 45
Iterative: n = 45
Analytical: n = 45
F(n) = 1134903170
F(n) = 1134903170
F(n) = 1134903170
(146.64 sec)
(0.00 sec)
(0.00 sec)
Recursive: n = 46
Iterative: n = 46
Analytical: n = 46
F(n) = 1836311903
F(n) = 1836311903
F(n) = 1836311903
(237.25 sec)
(0.00 sec)
(0.00 sec)
fig05_30.c
(4 von 4)
 2006 Pearson Education,
Inc. All rights reserved.
87
Fig. 5.31 | Einige rekursive Aufrufe der fibonacci Funktion.
 2006 Pearson Education, Inc. All rights reserved.
88
5.12 Fallstudie: Fibonacci-Reihe
• Vorsicht bei rekursiven Programmen
– Jeder Rekursionsschritt in der Funktion fibonacci
verdoppelt die Anzahl der Funktionsaufrufe:
• Die Anzahl der rekursiven Aufrufe zur Berechnung
der nten Fibonacci-Zahl ist von der Ordnung 2n,
abgekürzt geschrieben: O(2n ).
• Die 20te Fibonacci-Zahl benötigt O(220) oder etwa eine
Million Aufrufe.
• Die 30te Fibonacci-Zahl benötigt O(230) oder etwa eine
Milliarde Aufrufe.
– Exponentielle Komplexität
• kann selbst die leistungsfähigsten Rechner alt aussehen
lassen.
 2006 Pearson Education, Inc. All rights reserved.
89
Tipp zur Performanz
Rekursive Programme, die exponentielle
Komplexität haben (so wie die gezeigte
Berechnung von Fibonacci-Zahlen), sollten
vermieden werden.
 2006 Pearson Education, Inc. All rights reserved.
90
5.13 Fallstudie: Türme von Hanoi
• Türme von Hanoi
– Kombinatorisches Spiel, das Edouard Lucas auf der
Pariser Weltausstellung 1889 ausstellte.
– Geschichte dazu: Auf der ersten von drei Säulen im
Tempel von Benares(!) stecken 64 goldene, von unten nach
oben immer kleiner werdende Scheiben.
– Die Scheiben sollen nach folgenden Regeln von der ersten
auf die dritte Säule umgesetzt werden:
• Die Scheiben dürfen nur einzeln umgesetzt werden.
• Eine Scheibe darf nie auf eine kleinere gelegt werden.
• Zum Umbau dürfen alle drei Säulen benutzt werden.
 2006 Pearson Education, Inc. All rights reserved.
91
5.13 Fallstudie: Türme von Hanoi
• Schritt 0: Startposition
 2006 Pearson Education, Inc. All rights reserved.
92
5.13 Fallstudie: Türme von Hanoi
• Schritt 2:
 2006 Pearson Education, Inc. All rights reserved.
93
5.13 Fallstudie: Türme von Hanoi
• Schritt 4:
 2006 Pearson Education, Inc. All rights reserved.
94
5.13 Fallstudie: Türme von Hanoi
• Schritt 7:
 2006 Pearson Education, Inc. All rights reserved.
95
5.13 Fallstudie: Türme von Hanoi
• Schritt 8 :
 2006 Pearson Education, Inc. All rights reserved.
96
5.13 Fallstudie: Türme von Hanoi
• Schritt 11:
 2006 Pearson Education, Inc. All rights reserved.
97
5.13 Fallstudie: Türme von Hanoi
• Schritt 12:
 2006 Pearson Education, Inc. All rights reserved.
98
5.13 Fallstudie: Türme von Hanoi
• Schritt 15: Endposition
 2006 Pearson Education, Inc. All rights reserved.
99
5.13 Fallstudie: Türme von Hanoi
• Gewünschte Konsolenausgabe:
 2006 Pearson Education, Inc. All rights reserved.
100
5.13 Fallstudie: Türme von Hanoi
1 Gib numberOfDiscs ein
2 Rufe moveStack( numberOfDiscs,'A','B','C') auf
3
4 Definition von moveStack( count, source, dummy, destination ):
5
6
7
falls count gleich Eins ist
gib aus "Scheibe Eins von source nach destination"
sonst
8
Rufe moveStack( count-1, source, destination, dummy ) auf
9
gib aus "Scheibe count von source nach destination"
10
Rufe moveStack( count-1, dummy, source, destination ) auf
Pseudocode: Rekursiver Algorithmus für die Türme von Hanoi.
 2006 Pearson Education, Inc. All rights reserved.
101
5.14 Rekursion und Iteration
• Basieren auf unterschiedlichen Kontrollanweisungen
– Iteration: Wiederholungsstruktur
– Rekursion: Auswahlstruktur
• Realisierung der Wiederholung von Anweisungen
– Iteration: expliziter Gebrauch der Wiederholungsstruktur
– Rekursion: wiederholte Funktionsaufrufe
• Endbedingung
– Iteration: Schleifentest wird false
– Rekursion: Erreichen des Basisfalls (base case)
 2006 Pearson Education, Inc. All rights reserved.
102
5.14 Rekursion und Iteration
• Graduelle Annäherung an das Endergebnis
– Iteration: Änderung des Schleifenzählers
– Rekursion: Erzeugung von immer einfacheren Versionen
der Problemstellung
• Möglichkeit der Endloswiederholung
– Iteration: Schleifentest wird nie false
– Rekursion: Rekursionsschritt konvergiert nicht zum
Basisfall
 2006 Pearson Education, Inc. All rights reserved.
103
5.14 Rekursion und Iteration
• Stellen zwei unterschiedliche Programmierstile dar:
– Iteration: Imperative Programmierung
• Die Schritte zur Lösung des gegebenen Problems werden
detailliert aufgeschrieben.
• Der Programmcode selbst stellt schon die Lösung des Problems
dar; beim Programmablauf werden dann nur noch konkrete
Werte ermittelt.
– Rekursion: Deklarative Programmierung
• Das zu lösende Problem wird im Programmcode beschrieben.
• Die Lösung ergibt sich erst während des Programmablaufs zur
Laufzeit.
 2006 Pearson Education, Inc. All rights reserved.
104
5.14 Rekursion und Iteration
• Vorteil der Rekursion
– Sehr einfache Formulierung der Lösung bestimmter
Problemstellungen
• Nachteile der Rekursion
– Hoher Aufwand durch wiederholte Funktionsaufrufe
• Jeder Rekursionsschritt führt zu neuen Kopien aller lokalen
Variablen der Funktion.
• Kann (zu) viel Prozessorzeit und Speicherplatz kosten
 2006 Pearson Education, Inc. All rights reserved.
105
Betrachtung zum Software Engineering
Jedes Problem, das rekursiv gelöst werden
kann, kann auch iterativ gelöst werden
(und umgekehrt).
Ein rekursiver Ansatz ist dann sinnvoll, wenn
er die Problemstellung direkt widerspiegelt
und zu einem Programm führt, das leichter zu
verstehen und zu debuggen ist.
 2006 Pearson Education, Inc. All rights reserved.
106
Tip zur Performanz
Wenn es auf die Performanz eines Programms
ankommt, sollte Rekursion vermieden werden.
Rekursive Funktionsaufrufe brauchen Zeit und
zusätzlichen Speicherplatz.
 2006 Pearson Education, Inc. All rights reserved.
Herunterladen