Aufgaben zum Praktikum Programmieren PRP1

Werbung
Aufgaben zum Praktikum Programmieren PRP1
Prof. Dr. Thomas Klinker
Department Informations- und Elektrotechnik, HAW Hamburg
16. September 2013
1
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
1
Aufgabe 1a:
Geben Sie das Programm auf der nächsten Seite ein, bringen Sie es zum laufen und testen Sie es.
Aufgabe 1b:
Schreiben Sie ein Programm (ganz ähnlich dem in Aufgabe 1a), welches die Höhe h und den Radius r
eines Zylinders einliest und dann die gesamte Oberfläche und das Volumen des Zylinders berechnet.
Aufgabe 1c:
Schreiben Sie ein Programm, welches für eine positive ganze Zahl vom Typ long die Quersumme
berechnet. Die Quersumme von 1339 bespielsweise ist 16.
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
/********************************************************************
Autor:
Datum:
Dateiname:
klk
03.03.03
aufg01a1.cpp
Kurzbeschreibung: Kugelradius einlesen,
Oberfläche und Volumen berechnen,
auf ein Zeichen warten.
Änderungen:
Name
Datum
Kurzbeschreibung
-----------------------------------------------------------------klk
04.03.03
... Beseitigung des Fehlers ....
*********************************************************************/
#include <stdio.h>
#include <math.h>
/* für pow(x, y) */
int main(void)
{
const float pi = 3.1415927;
float radius, oberflaeche, volumen;
printf("Kugelradius in Metern: "); scanf("%f", &radius);
oberflaeche = 4 * pi * radius * radius;
printf("Oberfläche in Quadratmetern: %f\n", oberflaeche);
volumen = (4.0/3) * pi * pow(radius, 3);
printf("Volumen in Kubikmetern: %f\n", volumen);
return 0;
}
2
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
3
Aufgabe 2a:
Schreiben Sie ein Programm, das eine Folge von reellen Zahlen vom Typ float einliest. Das Ende
der Zahlenfolge wird erkannt durch das erste Zeichen, welches keine Zahl (also z.B. ein Buchstabe)
ist.
Das Programm soll dann für die eingelesenen Zahlen folgende Größen berechnen:
1. Die Anzahl der eingelesenen Zahlen,
2. die Summe der eingelesenen Zahlen,
3. das Maximum der eingelesenen Zahlen,
4. das Minimum der eingelesenen Zahlen,
5. den Mittelwert der eingelesenen Zahlen,
6. die Standardabweichung der eingelesenen Zahlen.
Die eingelesenen Zahlen seien mit xi (i = 1, . . . , n) bezeichnet. Der Mittelwert ist dann definiert
durch:
n
P
xi
i=1
x̄ =
.
(1)
n
Für die Standardabweichung gilt:
v
σ=
uP
u n
u (xi − x̄)2
t i=1
n−1
(2)
.
Sie werden feststellen, daß bei der Berechnung der Standardabweichung mit der Formel (2) das Problem auftritt, daß man sich alle eingelesenen Zahlen xi “merken“ (d.h. abspeichern) muß. Man kann
dieses Problem vermeiden, indem man die Standardabweichung mit folgender Formel berechnet
σ=
v
u P
u n 2
x − n · x̄2
u
t i=1 i
n−1
,
(3)
die darüber hinaus für die Bearbeitung von Zahlenfolgen beliebiger Länge geeignet ist.
Aufgabe 2b:
Schreiben Sie ein Programm, das die kartesischen Koordinaten x und y eines Punktes in der Ebene
einliest und sie in Polarkoordinaten r und ϕ (00 < ϕ ≤ 3600 ) umrechnet. Benutzen Sie für die
Berechnung des Winkels ϕ die atan2(y, x)–Funktion von C. Testen Sie Ihr Programm auch für
x = 0.
Umgekehrt soll das Programm auch die Eingabe von Polarkoordinaten gestatten und diese in
kartesische Koordinaten umrechnen gemäß den Formeln
x = r cos ϕ ,
y = r sin ϕ .
Der Winkel ϕ soll dabei jeweils im Gradmaß (00 < ϕ ≤ 3600 ) ausgegeben (bzw. eingelesen) werden.
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
4
Aufgabe 3a:
Schreiben Sie ein Programm, das eine Ziehung der Lotto-Zahlen simuliert. Wie allgemein bekannt,
befinden sich beim Ziehen der Lotto-Zahlen in einer Schale 49 Kugeln. Die Kugeln sind durchnummeriert von 1 bis 49. Nacheinander wird nun zufällig eine Kugel herausgenommen und die Zahl
auf der Kugel notiert. Wichtig: Die jeweils herausgenommene Kugel wird nicht zurück
gelegt! Insgesamt werden 6 Kugeln gezogen.
Das Ziehen einer Kugel soll mit Hilfe des Zufallsgenerators in C, also durch die Funktion
int rand(void) realisiert werden. rand( ) gibt einen int-Wert zurück. Dabei gilt für den Rückgabewert, dass er in dem Bereich 0 ≤ rand() ≤ 32767 liegt. Mit der Anweisung zahl = rand(); wird
der Variable zahl also eine Zufallszahl zwischen 0 und 32767 zugewiesen. Sie müssen für die Verwendung der Funktion int rand(void) die Header-Datei stdlib.h einbinden. Damit bei jedem
Programmdurchlauf immer eine neue Folge von Zufallszahlen berechnet wird, muss der Zufallsgenerator einmal (!) am Anfang des Programms initialisiert werden. Dies geschieht mit der Funktion
srand(...). Mit den folgenden Befehlen
#include <stdlib.h>
#include <time.h>
...
time_t t;
srand((unsigned) time(&t));
// Initialisiert den Zufallsgenerator
// am Anfang des Prgramms (!)
...
zahl = (rand() % 49) + 1;
...
// Erzeugt Zufallszahl zwischen 1 und 49
könnte man somit beispielsweise Zufallszahlen zwischen 1 und 49 erzeugen.
Achten Sie insbesondere darauf, dass jede Zahl nur einmal gezogen werden kann! Das Programm
soll eine wiederholte Ziehung der Lotto-Zahlen erlauben, solange es der Benutzer wünscht. Dabei
sollte sich der Ablauf des Programms auf der Konsole genauso abspielen, wie in nachfolgendem
Beispiel dargestellt.
Beispielhafter Ablauf des Programms auf der Konsole:
Die Lottozahlen lauten: 1
6
25
42
37
4
Soll dass Programm noch einmal ausgefuehrt werden (j/n): j
Die Lottozahlen lauten: 14
2
17
37
27
34
Soll dass Programm noch einmal ausgefuehrt werden (j/n): j
Die Lottozahlen lauten: 32
49
36
22
21
2
Soll dass Programm noch einmal ausgefuehrt werden (j/n): n
Press any key to continue
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
5
Aufgabe 3b:
In dieser Aufgabe soll ein bekanntes Streichholzspiel realisiert werden. Von einer Anfangsmenge
von Streichhölzern nehmen zwei Spieler abwechselnd eins, zwei oder drei Hölzchen weg. Wer das
letzte Hölzchen nehmen muß, hat verloren. Das Programm soll so gestaltet sein, daß ein Spieler
gegen den Computer spielt.
Der Spieler kann dabei bei jedem seiner Zügen nach Gutdünken wahlweise eins, zwei oder drei
Hölzchen nehmen. Die Züge des Computers können zunächst in einer ersten Version so realisiert,
daß er zufällig eins, zwei oder drei Hölzchen nimmt. (Hierbei verwende man den Zufallsgenerator
wie in Aufgabe 3a). Lediglich wenn nur noch vier Hölzchen oder weniger übrig geblieben sind, spielt
der Computer natürlich so, daß er gewinnt. Bei vier Hölzchen beispielsweise nimmt er drei, damit
der Spieler auf dem letzten Hölzchen sitzen bleibt.
Wenn Ihr Programm läuft, denken Sie nun einmal darüber nach, wie eine optimale Strategie für
den Computer aussieht. Der Computer soll also in einer verbesserten Version Ihres Programms von
Beginn an stehts den optimalen Zug ausführen. Er soll also immer so ziehen, daß er, wenn er die
Chance zu gewinnen hat, diese auch nutzt. Er spielt dann also mit maximaler Spielstärke.
Achten Sie darauf, daß auch der Spieler immer nur ein, zwei oder drei Hölzchen nehmen darf, nicht
mehr und nicht weniger. Zu Beginn des Programms soll man die Anfangszahl der Hölzchen, mit
der gespielt werden soll, eingeben können. Als nächstes soll man auswählen können, wer anfängt
zu ziehen, der Computer oder der Spieler.
Die Darstellung auf der Konsole könnte dann z.B. wie folgt aussehen:
Die aktuelle Anzahl der Hoelzchen ist: 13
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
*
|
|
|
Wieviele Hoelzchen nehmen Sie? (1, 2, 3):
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
6
Aufgabe 4a:
Schreiben Sie ein Programm, das das Pascalsche Dreieck berechnet. Im Pascalschen Dreieck sind
die Binominalkoeffizienten
!
n!
n
(4)
=
m
(n − m)! m!
pyramidenförmig angeordnet.
1
1
1
2
1
1
1
1
1
3
4
5
6
1
3
6
10
15
1
4
1
10
20
5
1
15
6
1
Statt mit Gl. (4) lassen sich die Binominalkoeffizienten für alle n ≥ 0 und m = 0, . . . , n schneller
nach folgenden Formeln berechnen:
n
0
!
n
m
!
=
n
n
!
= 1
=
n−1
m−1
!
+
n−1
m
!
m = 1, . . . , n − 1 .
,
Benutzen Sie diese Formeln und verwenden Sie zur Speicherung der Binominalkoeffizienten ein
zweidimensionales Feld b[n][m], so daß gilt
b[n][m] =
n
m
!
.
(5)
Aufgabe 4b:
Ein magisches Quadrat der Ordnung n ist eine quadratische Anordnung der Zahlen 1, 2, . . . , n2 , so
daß alle n Zeilen– und Spalten– sowie die Hauptdiagonalsummen gleich sind.
Schreiben Sie ein C–Programm, das nach Eingabe einer ungeraden natürlichen Zahl n ein magisches
Quadrat der Ordnung n berechnet und auf dem Bildschirm ausdruckt. Verwenden Sie dazu folgende
Methode:
Die Zahlen 1, 2, . . . , n2 werden fortlaufend in das Quadrat eingetragen. Beginnen Sie damit, daß
Sie die 1 in die Mitte der ersten Zeile eintragen. Gehen Sie dann jeweils eine Spalte nach links und
eine Zeile nach oben. Verwenden Sie dabei periodische Randbedingungen, das heißt, wenn Sie über
die oberste Zeile hinauskommen, fahren Sie mit der untersten fort, und wenn Sie über den linken
Rand hinauskommen fahren Sie rechts fort. Trifft man auf ein schon besetztes Feld, so gehe man
statt dessen ein Feld nach unten und fahre dort fort. Dieses Verfahren ist in dem nachfolgenden
Bild dargestellt am Beispiel eines magischen Quadrates der Ordnung 5.
Programmieren in C - Teil I,
Praktikumsaufgaben,
15
16
22
3
9
8
14
20
21
2
1
7
13
19
25
24
5
6
12
18
Prof. Dr. Thomas Klinker
7
17
23
4
10
11
Das Programm soll magische Quadrate bis zur Ordnung n = 19 berechnen und auf der Konsole
können. Prüfen Sie anhand des obigen Beispiels für n = 5, ob Ihr Programm korrekt arbeitet.
Programmieren in C - Teil I,
Praktikumsaufgaben,
8
Prof. Dr. Thomas Klinker
Aufgabe 5:
Im Jahr 1968 wurde von J. H. Conway an der Universität Cambridge das “game of life“ erfunden und
1970 von M. Gardner im Scientific American einem breiten Publikum vorgestellt. Dabei handelt
es sich um einen Algorithmus, der das Wachstum von fiktiven Lebewesen (Bakterien) simuliert.
Infolge der interessanten Muster, die dabei entstehen, ist das game of life weit über Biologenkreise
hinaus bekannt geworden. Es ist ein Beispiel für einen sogenannten zellulären Automaten.
Schauplatz des game of life ist ein zweidimensionales Gitter aus Zellen, die entweder tot (’ ’) oder
lebendig (’X’) sind. Wie sich eine Zelle weiter entwickelt, hängt von ihren acht Nachbarn ab, und
zwar gelten folgende Regeln:
1. Eine lebende Zelle überlebt in der nächsten Generation, wenn sie zwei oder drei Nachbarn
hat. Sind es weniger bzw. mehr, so stirbt sie an Vereinsamung bzw. Überbevölkerung.
2. Eine tote Zelle wird immer dann in der nächsten Generation zum Leben erweckt, wenn sie
genau drei lebendige Nachbarn hat, ansonsten bleibt sie tot.
Die Zeit verstreicht dabei in diskreten Schritten, d.h. jede Zelle verharrt in ihrem zuvor eingenommenen Zustand, bis gewissermaßen bei einem Gongschlag alle gleichzeitig in den neuen Zustand
übergehen. Anders ausgedrückt: Es wird für jede Zelle nachgeschaut, wie ihr Zustand und der ihrer
Nachbarn zu einer bestimmten Zeit n ist und berechnet, wie ihr Zustand zur nächsten Zeit n + 1
sein wird. Hat man dies für alle Zellen getan, so werden alle gleichzeitig auf den neuen Zustand
gesetzt. In der abschliessenden Version sollte Ihr Programm periodische Randbedingungen für das
zweidimensionale Feld benutzen (genauere Erklärungen hierzu und Vorschläge zur Realisierung der
periodischen Randbedingungen werden noch in der Vorlesung gegeben).
Gestalten sie das Programm so, daß man als Ausgangsmuster entweder eins der folgenden fünf
auswählen kann, oder über die Angabe der Koordinaten (Zeile, Spalte) ein eigenes Ausgangsmuster
lebendiger Zellen eingeben kann.
XXX
Blinker
XX
XX
Block
X
X X
X X
X
Bienenstock
XX
X
XX
X X
X
Leuchtfeuer
Gleiter
X
XX
Jede neue Generation soll auf dem Bildschirm ausgegeben werden, wobei die vorhergehende Generation immer durch die aktuelle überschreiben werden soll. Um dieses einfach bewerkstelligen zu
können, werden Ihnen zwei Dateien zur Verfügung gestellt, console.h und console.cpp , die
Sie Ihrem Projekt hinzufügen müssen. Damit verfügen Sie über folgende Funktionen:
bool setCursor(int zeile, int spalte)
/* Funktion, die Cursor auf die angegebene Position (zeile, spalte) setzt.
bool cls()
/* Funktion, die Consolen-Puffer mit aktuellem Attribut löscht
/* und Cursor auf die Home-Position (0,0) setzt (clear screen).
*/
*/
*/
Programmieren in C - Teil I,
Praktikumsaufgaben,
void cursorOff()
/* Funktion, die den Cursor unsichtbar macht.
Prof. Dr. Thomas Klinker
9
*/
void cursorOn()
/* Funktion, die den Cursor wieder sichtbar macht. */
DWORD cursorSize(DWORD size)
/* Funktion, die die CursorGröße setzen kann (in Prozent).
*/
Wählen sie für das game of life ein Feld der Größe: 22 Zeilen und 78 Spalten. Über dem Feld
sollte die Nummer der aktuellen Generation angezeigt werden. Auf diese Weise wird die Größe
des Bildschirms gut ausgenutzt. Die Größe des Konsolen-Fensters kann allerdings auch geändert
werden, so daß man das game of life auf einem größeren Spielfeld ablaufen lassen kann (Weitere
Hinweise in der Vorlesung).
Das fortlaufende Anzeigen immer neuer Generationen kann wahweise durch das Drücken einer Taste
erfolgen oder automatisch durch die folgende Schleifenkonstruktion
while(!_kbhit())
{
statement;
...
}
// Weitermachen, bis irgendeine Taste gedrueckt wird
Hierbei werden die neuen Generationen automatisch angezeigt, bis irgendeine Taste gedrückt wird.
Damit die Generationen nicht zu schell nacheinander angezeigt werden, verwenden Sie bei obiger Schleifenkonstruktion die Funktion Sleep(n) (deklariert im Headerfile <windows.h>), die eine
Verzögerung des Programmablaufs um n Millesekunden bewirkt.
Wer noch Lust hat, mit dem Programm etwas zu spielen, kann folgende Anfangskonfiguration
eingeben, die als Gleiterkanone bezeichnet wird.
6
7
8
9
0
1
X
2
X X
3
X
XX
X
4 XX
X
XX
XXXX
X
5 XX
X
XX
XXXX
X
6
X X
X X
XX
7
X
XXXX
XX
8
XXXX
9
X
0
123456789012345678901234567890123456789012345678901234567890123456789
Die Gleiterkanone wurde 1970 entdeckt von R. W. Gosper, der damals Student am Massachusetts
Institut of Technologie (MIT) war, und sich unbedingt den 50-Dollar-Preis verdienen wollte, den
Programmieren in C - Teil I,
Praktikumsaufgaben,
Prof. Dr. Thomas Klinker
10
Conway ausgelobt hatte. Den Preis sollte der erhalten, der als erster beweisen würde, daß eine
Ausgangsstellung unbegrenzt wachsen kann. Gospers Gleiterkanone spuckt jeweils nach 30 Zeitschritten einen neuen Gleiter aus, wobei sie selbst in den Ausgangszustand zurückkehrt. Auf der
Basis der Gleiterkanone lassen sich logische Gatter, also NOT-, AND- und OR-Gatter und damit
auch Rechner konzipieren.
Herunterladen