DAP2 Praktikum – Blatt 0 - Chair 11: ALGORITHM ENGINEERING

Werbung
Fakultät für Informatik
Lehrstuhl 11 / Algorithm Engineering
Prof. Dr. Petra Mutzel, Carsten Gutwenger
André Gronemeier, Tobias Marschall, Hoi-Ming Wong
Sommersemester 2008
DAP2 Praktikum – Blatt 0
Ausgabe: 8. April — Abgabe: keine
Dieses Praktikumsblatt dient zur Vorbereitung und wird nicht bewertet. Trotzdem solltet ihr
es auf jeden Fall in den Praktikumsgruppen bearbeiten, da euch dann die Bearbeitung der
nächsten Praktikumsblätter deutlich leichter fallen wird. Die ersten Schritte in C++ erleichtert euch das Skript Von Java nach C++ von Thomas Willhalm (siehe http://i11www.iti.
uni-karlsruhe.de/teaching/scripts/sources/java2c++.pdf).
Aufgabe 0.1
Schreibe ein C++-Programm, das "Hello, C++!" mit dem C++-Ausgabestream cout ausgibt.
Hinweise:
• Verwende für deine main-Funktion die Signatur int main().
• Um die C++ Ausgabestreams verwenden zu können, musst du mit #include <iostream>
zu Beginn der Quelldatei den entsprechenden Header inkludieren.
• Die Symbole der C++-Ausgabestreams befinden sich im Namespace std. Daher ist es
notwendig, dem Ausgabestream cout ein std:: voranzustellen, also std::cout. Alternativ können auch mit using namespace std; (vor der main-Funktion) alle Symbole des
Namensraums std in den globalen Namensraum überführt werden.
• Benutzt du den g++ Compiler, dann kannst du deine Quelldatei aufgabe 0 1.cpp mit
g++ aufgabe 0 1.cpp -o aufgabe 0 1 compilieren und linken. Dabei wird mit der -o
Option der Name des erzeugten Programms angegeben.
Aufgabe 0.2
Schreibe ein Programm, das mit zwei positiven, ganzzahligen Eingabeparametern a und b aufgerufen wird und den größten gemeinsamen Teiler von a und b ausgibt. Wird das Programm
nicht korrekt aufgerufen, so soll es eine Fehlermeldung mit einer Beschreibung des korrekten
Aufrufs ausgeben.
Benutze dazu Euklids Algorithmus, der sehr einfach rekursiv wie folgt formuliert werden kann:
function Euclid(a, b)
if b = 0 then
return a
else
return Euclid(b, a mod b)
end if
end function
Versuche auch, den Algorithmus ohne Rekursion zu implementieren.
Hinweise:
• Verwende für deine main-Funktion die Signatur int main(int argc, char *argv[]).
Dabei ist argc die Anzahl der übergebenen Argumente (inklusive Programmname!) und
argv ein C++-Array mit argc Elementen.
• Die Funktion int atoi(const char *str) wandelt einen C++-String in einen Integer
um; dazu muss vorher der Header <cstdlib> inkludiert werden.
Aufgabe 0.3
Schreibe ein Programm, das die Zahl π mit Hilfe des Gauss-Legendre Algorithmus auf eine
vorgegebene Anzahl von Stellen genau berechnet. Dieser Algorithmus ist gegeben durch folgende
Iterationsvorschrift:
(
1
+ bi−1 )
√
(
für
1/ 2
√
=
ai−1 bi−1 für
ai =
bi
1
(a
2 i−1
i=0
i>0
(
1/4
ti−1 − pi−1 (ai−1 − ai )2
(
1
2pi−1
ti =
pi =
für i = 0
für i > 0
für i = 0
für i > 0
für i = 0
für i > 0
Dann ergibt sich die Näherung für π nach n Iterationsschritten als
π≈
(an + bn )2
.
4tn
Die Anzahl der korrekten Stellen in der Näherung verdoppelt sich dabei in jedem Iterationsschritt.
Die tatsächliche Genauigkeit der Lösung ist natürlich durch den verwendeten Datentyp (float,
double) für Fließkommazahlen beschränkt. Der g++ Compiler unterstützt auch 128-bit Fließkommazahlen mit dem Datentyp long double.
Hinweise:
• Funktionen für Fließkommazahlen wie sqrt (Quadratwurzel) befinden sich im Header
<cmath>.
• Man kann die Anzahl der ausgegebenen Stellen für Fließkommazahlen beim C++-Ausgabestream mit setprecision (im Header <iomanip>) angeben, z.B.
cout << setprecision(10) << sqrt(2.0) << endl;
Aufgabe 0.4
Schreibe ein Programm, das für einen Eingabeparameter n mit Hilfe des Sieb des Eratosthenes
die Primzahlen bis n berechnet und ausgibt. Dieser Algorithmus legt zunächst ein boolesches
Array isPrime an, das mit den Zahlen von 2, . . . , n indiziert werden kann und mit true initialisiert ist. Das bedeutet, zunächst sind alle Zahlen potentielle Primzahlen. Danach werden die
Zahlen von i = 2, . . . , n durchgegangen. Falls isPrime[i] true ist, dann ist i tatsächlich eine
Primzahl und für alle Vielfachen j > i von i wird isPrime[j] auf false gesetzt.
Hinweise:
• Lege das dynamische Array isPrime mit Hilfe des new-Operators an, z.B. legt int *a =
new int[10]; ein Array a mit Indexbereich [0..9] an.
• Denke daran, den dynamisch allozierten Speicher auch wieder freizugeben! Benutze dazu
die Array-Variante des delete-Operators, z.B. delete [] a;.
Aufgabe 0.5
Implementiere eine Klasse SinglyLinkedList, welche einfach verkettete Listen für doubleZahlen repräsentiert. Diese Klasse soll eine Iterator-Klasse SLLIterator verwenden; Instanzen
dieser Klasse verweisen entweder auf ein Element der Liste oder stellen einen Null-Iterator dar,
der auf kein Element verweist.
Die Schnittstellen der beiden Klassen sehen wie folgt aus:
class SLLIterator {
public:
SLLIterator(); // Erzeugt Iterator, der auf kein Listenlement zeigt.
SLLIterator(const SLLIterator &it); // Erzeugt Kopie von Iterator it.
// Gibt true zurück, wenn der Iterator auf ein Listenelement zeigt.
bool valid() const;
bool operator==(const SLLIterator it) const;
// Test auf Gleichheit.
bool operator!=(const SLLIterator it) const;
// Test auf Ungleichheit.
SLLIterator &operator=(const SLLIterator &it); // Zuweisungsoperator.
// Gibt eine Referenz auf den Inhalt des Elementes zurück, auf das der Iterator zeigt.
// Vorbedingung: Iterator zeigt auf ein Listenlement.
double &operator*() const;
// Präfix-Inkrementoperator:
// Vorbedingung: Iterator zeigt auf ein Listenlement.
// Verschiebt den Iterator zum nachfolgenden Element und gibt ihn zurück.
SLLIterator &operator++();
// Postfix-Inkrementoperator:
// Vorbedingung: Iterator zeigt auf ein Listenlement.
// Gibt einen Kopie des Iterators zurück und verschiebt ihn danach zum nachfolgenden
// Element. Der int-Parameter ist nur ein Dummy, um die Postfix-Notation zu
// kennzeichnen; er wird nie benutzt.
SLLIterator operator++(int);
};
class SinglyLinkedList {
public:
SinglyLinkedList(); // Erzeugt eine leere Liste.
SinglyLinkedList(const SinglyLinkedList &L); // Erzeugt eine Kopie von Liste L.
bool empty() const;
// Gibt wahr zurück, falls die Liste leere ist.
SLLIterator begin () const; // Gibt Iterator auf erstes Element zurück.
SLLIterator rbegin() const; // Gibt Iterator auf letztes Element zurück.
void clear(); // Löscht alle Elemente in der Liste.
SinglyLinkedList &operator=(const SinglyLinkedList &L); // Zuweisungsoperator.
SLLIterator pushFront(double x); // Fügt x am Anfang der Liste hinzu.
SLLIterator pushBack (double x); // Fügt x am Ende der Liste hinzu.
// Löscht das erste Element der Liste und gibt dessen Inhalt zurück.
// Vorbedingung: Die Liste ist nicht leer.
double popFront();
};
// Ausgabeoperator für Listen.
std::ostream &operator<<(std::ostream &os, const SinglyLinkedList &L);
Schreibe die Deklaration der beiden Klassen in eine Datei SinglyLinkedList.h und die Implementierung in SinglyLinkedList.cpp. Schreibe ein Hauptprogramm, das für einen Eingabeparameter n eine Liste mit n zufälligen double-Werten erzeugt und mit Hilfe des Ausgabeoperators
ausgibt. Benutze dabei ein Makefile, das die .cpp-Dateien einzeln in Objektdateien compiliert
und diese dann linked.
Hinweise:
• Die Funktion rand() in <cstdlib> gibt eine ganzzahlige Zufallszahl aus dem Bereich
[0..RAND MAX] zurück.
• Verwende das Compilerflag -Wall, um alle Warnungen auszugeben; dein Quellcode sollte
ohne Warnungen compilieren.
• Achte in deinem Makefile darauf, dass die Aktionen, die für ein Ziel ausgeführt werden
sollen (z.B. Aufruf des Compilers), durch Tabs (und nicht Leerzeichen!) eingerückt sein
müssen.
• Funktionen, deren Implementierung nur sehr wenig Code erfordert (z.B. Einzeiler), können
ihr auch direkt in der Klassendeklaration angeben werden; komplexere Implementierungen
sollten aber auf jeden Fall in die .cpp-Datei ausgelagert werden.
• Du wirst den Speicher für Listenelemente beim Hinzufügen von Elementen dynamisch
mit new allozieren müssen. Denke daran, diesen auch beim Entfernen von Elementen und
im Destruktor wieder mit delete freizugeben.
Vill Glèck!
– das DAP2-Team
Herunterladen