Aufgabe 1: Eigenschaften von Algorithmen I Ordnen Sie die Eigenschaften terminierend, determiniert und deterministisch den folgenden Algorithmen zu: a) 1. Lese zwei Zahlen p und q aus Z aus der Datei Eingabe ein. 2. Stelle eine Variable erg bereit, die alle Werte aus Z annehmen kann. 3. Wenn q 0, dann sei erg = p % q, Sonst sei erg = -1. (% = Modulo = Rest der ganzzahligen Division) 4. Gib erg aus b) 1. Generiere eine zufällige ganze Zahl zwischen -10 und 20. 2. Multipliziere diese Zahl mit sich selbst. 3. Gib das Ergebnis aus, falls es größer als 10 ist. Andernfalls gehe zu 2. 1
Aufgabe 2: Eigenschaften von Algorithmen II Ordnen Sie die Eigenschaften terminierend, determiniert und deterministisch der folgenden Funktion zu. Treffen Sie gegebenenfalls Fallunterscheidungen für verschiedene Eingabewerte. float rechne(float a) float p; if (a > 10) p = (float)rand()/rand_max; // erzeugt Zufallszahl zwischen 0 und 1 if( a > 20 ) a = a-p; a = a+p; while (a < 2) a = a*a*a; return a; 2
Aufgabe 3: Einstiegsprogrammieraufgaben 1. Durchschnitt Schreiben Sei eine Funktion, welche den Durchschnittswert aller ganzen Zahlen innerhalb eines definierten Bereichs berechnet. (a) Welche Eingabewerte benötigt der Algorithmus und wie lautet der Rückgabewert? Geben Sie den Funktionskopf an. (b) Formulieren Sie das nötige Vorgehen in Worten. (c) Erstellen Sie die Funktion. 2. Quersumme Entwerfen Sie eine Funktion zur Berechnung der Quersumme einer Zahl z Z. (a) Überlegen Sie sich einen Lösungsansatz. (b) Formulieren Sie Ihren Ansatz als Algorithmus in Worten. (c) Wie lautet der Funktionskopf für die zu implementierende Funktion? Wie lauten die Parameter und der Rückgabewert? (d) Implementieren Sie die Funktion und ein kurzes Hauptprogramm. 3. Potenz Schreiben Sie eine Funktion, welche die Potenz a b berechnet. Sowohl Basis a R als auch Exponent b Z sollen vom Benutzer abgefragt werden. (a) Welche Vorgehensweise dient zur Lösung des Problems? (b) Welche Werte müssen der Funktion übergeben werden? Welche Werte werden zurückgegeben? (c) Schreiben Sie die Funktion und das passende Hauptprogramm inkl. Benutzereingabe. 3
Aufgabe 4: Matrizenrechnung Implementieren Sie die Matrixoperationen Transponierung und Multiplikation als C++- Funktionen. Die Eingangsmatrizen sollen float-werte enthalten und jeweils die feste Größe N N haben. N sei eine global verfügbare Variable. 1. Definieren Sie die nötigen Datenstrukturen zur Darstellung der folgenden Matrizen: (a) Matrix E. (b) Transponierte Matrix T. (c) Multiplikationsmatrix F. (d) Multiplikationsergebnis M. Sämtliche von Ihnen definierten Datenstrukturen seien von nun an global verfügbar. 2. Schreiben Sie die Funktionen void Transpose() zur Transponierung der Matrix E. Das Ergebnis ist in T zu speichern. 3. Implementieren Sie die Funktion void Multiply() zur Multiplikation der Matrizen E und F. Das Ergebnis ist in M zu speichern. 4. Geben Sie die Anzahl der Arbeitsschritte in den beiden Funktionen an. 4
Aufgabe 5: Suchalgorithmen Diese Aufgabe beschäftigt sich mit einfachen Suchalgorithmen. Als Informationsspeicher steht ein Array des folgenden Datentyps zur Verfügung: struct Buch string titel; string autor; float preis; bool signiert; ; Buch Regal[100]; 1. Schreiben Sie eine Funktion, welche mittels sequenzieller Suche ein Buch mit einem bestimmten Titel im Array Regal findet. Schätzen Sie die Komplexität des Algorithmus ab. Wieviele Vergleiche werden durchschnittlich benötigt, um ein Regal mit 10.000 Büchern zu durchsuchen? 2. Nun kann davon ausgegangen werden, dass die Bücher alphabetisch nach dem Titel sortiert im Array vorliegen. In diesem Fall bietet sich die binäre Suche zur Auffindung eines bestimmten Eintrags an. Schreiben Sie die zugehörige Funktion und schätzen Sie ihre Komplexität ab. Wieviele Vergleiche werden maximal benötigt, um ein Regal mit 10.000 Büchern zu durchsuchen? 5
Aufgabe 6: Notenspiegel Es soll eine Software zur Verwaltung des Notenspiegels von Klausurergebnissen entwickelt werden. Es wurde vereinbart, die erzielten Klausurnoten in dem globalen Array int Notenspiegel[N] abzulegen. N ist eine ebenfalls globale Konstante, die die Anzahl der Klausurteilnehmer angibt. Es existieren die Noten 0, 1, 2, 3, 4, 5 und 6 (0 bedeutet n.e.). 1. Entwickeln Sie die Funktion void Mittelwert(float &mittel), die in mittel den Mittelwert der Klausurergebnisse zurückgibt. Die Klausurnoten sollen nun der Größe nach sortiert werden. Dazu ist das schnelle Sortierverfahren Bucketsort einzusetzen. Es dient zum Sortieren von Zahlenfolgen, bei denen vorher feststeht, dass jede der möglichen Zahlen innerhalb eines festen Wertebereichs liegt. Im aktuellen Fall ist dieser [0 6]. Die Vorgehensweise ist die folgende: Für jeden möglichen Wert wird ein Eimer (bucket) angelegt. Alle Eingangswerte werden durchlaufen und in den zugehörigen Eimer gelegt. Ausgabe der Werte in sortierter Reihenfolge mittels Durchlaufen aller Eimer von vorne nach hinten. 2. Implementieren Sie Bucketsort in der Funktion float Sort(). Die sortierten Noten sollen wieder in Notenspiegel abgelegt werden. Zurückzugeben ist der Mittelwert der Klausurnoten, welcher über die Funktion Mittelwert aus 1. bestimmt werden kann. 3. Wie viele Rechenschritte benötigt der Bucketsort-Algorithmus zur Sortierung der Eingangsdaten? 6
Aufgabe 7: Komplexität von Algorithmen Bestimmen Sie von den folgenden Algorithmen die genaue Anzahl der Aufrufe der Anweisung count() in Abhängigkeit von Parameter n. Geben Sie die Komplexitäten in der O-Notation an. eins(int n) int i, j, k; zwei(int n) int i, j; for(i=1; i<=n; i=i+1) for(j=1; j<=n; j=j+1) for(k=1; k<=n; k=k+1) count(); for(i=1; i<=n ; i=i+1) for(j=1; j<=n; j=j+1) if(i+j > n) count(); Hinweis: n i=1 i = 1 n(n + 1), 2 7
Aufgabe 8: Druckauftragverwaltung Drucker werden häufig von mehreren Anwendern genutzt. Dabei kommt es sehr schnell zu zeitraubenden Warteschlangen. Damit wichtige Dokumente pünktlich ausgedruckt werden, soll die Warteschlange auch die Priorität der Dokumente berücksichtigen. D.h., Druckaufträge mit hoher Priorität sollen bevorzugt bearbeitet werden. Für die Entwicklung dieser Steuerungssoftware müssen folgende Fragestellungen gelöst werden: 1. Für die Implementierung wurden sowohl eine lineare Liste als auch ein nach Priorität erstellter binärer Suchbaum vorgeschlagen. Geben Sie für beide Vorschläge den Aufwand für das Suchen des Auftrags mit höchster Priorität in O-Notation an. Begründen Sie kurz Ihre Antwort. 2. Sie sollen den Ansatz des binären Suchbaums weiter verfolgen. Definieren Sie eine geeignete Datenstruktur struct auftrag, in welcher zusätzlich zu für den Baum benötigten Elementen die Priorität und der Dokumententitel abgespeichert werden kann. 3. Schreiben Sie die Funktion void Ausgabe(auftrag* wurzel), welche die Dateinamen sämtlicher Elemente des Baumes mit ansteigender Priorität ausgibt. Um welche Traviersierungsart handelt es sich dabei? 4. Entwickeln Sie die rekursive Funktion void naechsterauftrag(auftrag* wurzel). Sie soll aus dem übergebenen Baum wurzel den Dateinamen des Dokuments mit der höchsten Priorität ausgeben. Beachten Sie, dass wurzel auch NULL sein kann. In dem Fall ist Leere Liste auszugeben. 5. Entwickeln Sie die rekursive Funktion string naechsterauftrag(auftrag* wurzel). Sie soll aus dem übergebenen Baum wurzel den Dateinamen des Dokuments mit der höchsten Priorität zurückgeben. Falls keine Aufträge existieren, ist Kein Auftrag zurückzugeben. 6. Implementieren Sie die rekursive Funktion int Hoehe(auftrag* w), welche die Höhe des Baumes mit der Wurzel w ermittelt und zurückgibt. 7. Implementieren Sie die rekursive Funktion int HoeheAusgeglichen(auftrag* w), welche die Höhe des Baumes mit der Wurzel w ermittelt und zurückgibt, sofern es sich um einen ausgeglichenen Baum handelt. Andernfalls ist -1 zurückzugeben. 8
Aufgabe 9: Cliquenbildung Gegeben ist der obige ungerichtete Graph. Es ergeben sich folgende Fragestellungen: 1. Wieviele 3er- und 4er-Cliquen existieren in dem Graphen? Nennen Sie die zugehörigen Knoten. 2. Definieren Sie eine geeignete Datenstruktur zur Speicherung eines Graphen mit N Knoten als Adjazenzmatrix. 3. Geben Sie die Adjazenzmatrix für den obigen Graphen an. Nicht existierende Kanten sollen durch 0 und Kanten durch 1 gekennzeichnet werden. 4. Nennen Sie eine alternative Repräsentation von Graphen (Name reicht). Wann sollte diese Alternative im Hinblick auf Speichereffizienz verwendet werden? 5. Entwickeln Sie die Funktion bool test3erclique(int k1, int k2, int k3), die überprüft, ob die Knoten mit den Nummern k1, k2 und k3 eine 3er-Clique im Graphen bilden. Wenn dies so ist, soll die Funktion true zurückgeben, andernfalls false. 6. Entwickeln Sie die Funktion int anz3ercliquen(). Sie soll unter Verwendung der Funktion test3erclique die Anzahl der im Graphen enthaltenden 3er-Cliquen zurückgeben. 9
Aufgabe 10: Straßenkarte A 8m B 5m 2m C 21m 7m D Es soll eine Software zur Analyse von Straßenkarten entwickelt werden. Da in ihr auch Einbahnstraßen vorkommen können, soll sie als gerichteter Graph repräsentiert werden. Dabei entspricht ein Knoten einer Weggabelung bzw. Kreuzung und die Kante einem Verbindungsweg. Die Weglänge ist als Kantenattribut hinterlegt. 1. Geben Sie für den nebenstehenden Graphen die Adjazenzmatrix an. Nicht existierende Kanten sollen mit -1 gekennzeichnet werden. 2. Erstellen Sie die nötigen Datenstrukturen für die Knoten und Kanten, um den Graphen als Adjazenzliste zu modellieren. 3. Skizzieren Sie mit Hilfe der vorgeschlagenen Datenstrukturen die Adjazenz-Listen (Verkettung der Zeiger) des in der obigen Abbildung dargestellten Graphen. 4. Schreiben Sie die Funktion void Ausgabe(knoten* erster), welche die Knotenbezeichnungen und sämtliche Kantenwerte auf dem Bildschirm ausgibt. Es ist geplant, eine Funktion int mindist(knoten* start, knoten* ziel) zu entwickeln. Sie soll die kürzeste Strecke zwischen den Knoten start und ziel ermitteln. Wenn der Knoten nicht erreichbar ist, soll 999999 zurückgegeben werden. Es ergeben sich folgende Fragestellungen: 5. Überlegen Sie sich eine Strategie zur Lösung des Problems. 6. Wie kann vermieden werden, dass der Algorithmus beim Durchlaufen des Graphen in eine Endlosschleife gerät? Erläutern sie das Prinzip und eventuelle Ergänzungen an der Datenstruktur. 7. Implementieren Sie die Funktion int mindist(knoten* start, knoten* ziel). 8. Mit welchem Aufwand ist in Abhängigkeit von der Knotenanzahl n zu rechnen? Geben Sie Ihre Abschätzung in O-Notation an und begründen Sie Ihre Antwort. 10
Aufgabe 11: Warensortiment In einem computergesteuerten Kassensystem sollen die Waren eines Supermarktes gespeichert werden. Das Sortiment umfasst maximal 10.000 Artikel. Für jedes Produkt sind der Name, die Nummer des Strichcodes und der Preis zu speichern. Beim Kassieren werden aufgrund des Strichcodes (welcher eine neunstellige Produktnummer codiert) die Produktbezeichnung und der Preis ermittelt. Um die Suche bei einer großen Anzahl von Produkten schnell durchführen zu können und um auf einfache Weise neue Waren in die Datenbasis aufnehmen zu können, bietet sich die Verwendung eines Hash-Verfahrens an. In den folgenden Teilaufgaben sollen geeignete Datenstrukturen und Algorithmen für das Kassensystem entworfen werden. 1. Geben Sie eine Hashfunktion an, die einer beliebigen Strichcodenummer einen Speicherplatz zuordnet. 2. Geben Sie geeignete Datenstrukturen für die Darstellung der Produktdaten und für die Hashing-Tabelle an. 3. Schreiben Sie void einfuegen(int code, string name, float preis) als Funktion zum Einfügen neuer Artikel in die Hashing-Tabelle. Kollisionen sollen durch lineares Sondieren behandelt werden. 4. Schreiben Sie eine Funktion float findepreis(int code), die für einen gegebenen Strichcode den Preis der zugehörigen Ware zurückgibt. 5. Um beim Ausfall einer Kasse weiter verkaufen zu können, soll zusätzlich eine Liste der Artikel in einer nach Artikelnummern sortierten Reihenfolge erstellt werden. Kann diese Funktion ohne weiteres mittels der nach dem Hashing-Verfahren erstellten Liste realisiert werden? 11
Aufgabe 12: Objektorientierte Programmierung Folgende Klassen seien deklariert: class A public: int a; int func1(); protected: int b; void func2(); private: int c; class B : public A public: int d; void func3(float &nr); protected: string s; 1. Auf welche Attribute können die Funktionen func1(), func2() und func3() jeweils zugreifen? 2. Auf welche Attribute kann mittels einer im Programm angelegten Variable B test von außen zugegriffen werden? 12
Aufgabe 13: Weinkeller Der Bestand eines Weinkellers soll mit einem Computer verwaltet werden. Dazu wird jeder Weinsorte eine 10-stellige eindeutige Identifikationsnummer (ID) zugewiesen. Sie dient zum schnellen Auffinden des Weines, wobei der Datenzugriff über eine Hashingtabelle erfolgen soll. Kollisionen sollen mit linearer Verkettung behoben werden. Die nachstehende Datenstruktur wird zur Speicherung einer Weinsortenbeschreibung vorgeschlagen: struct sorte int weinid; string bezeichnung; int jahrgang; int anzahl; sorte *next; ; // eindeutige Wein-ID // Bezeichnung des Weines // Jahrgang des Weines // Anzahl der noch verfügbaren Flaschen // Zeiger für lineare Verkettung Die Hashingtabelle soll die Größe N haben, die Software ist objektorientiert zu programmieren. Der gesamte Weinbestand eines Kellers soll durch ein Objekt der Klasse weinbestand repräsentiert werden. Nachfolgend ist ein Ausschnitt der Klassendeklaration dargestellt: class weinbestand public: sorte* getwine(int id); // liefert den Wein zur gewünschten id int getwineanzahl(int id); // liefert die Flaschenanzahl des Weines id protected: sorte* hashtab[n]; // Hashtabelle ; Sie werden mit den folgenden Teilaufgaben der Softwareentwicklung beauftragt: 1. Schlagen Sie eine geeignete Hashingfunktion h(id) vor. 2. Entwickeln Sie die Funktion sorte* weinbestand::getwine(int id). Sie soll den Zeiger auf den Wein mit der gewünschten id zurückgeben. 3. Implementieren Sie die Funktion int weinbestand::getwineanzahl(int id), die unter Verwendung der Funktion des vorherigen Aufgabenteils die Anzahl der im Weinkeller verfügbaren Flaschen des Weins mit der Kennung id zurückgibt. Wenn der gesuchte Wein nicht gefunden wurde, soll -1 zurückgegeben werden. Es soll eine Klasse weinbestandplus entwickelt werden. Sie stellt eine um die nachfolgend aufgelisteten zusätzlichen Funktionen erweiterte Version der Klasse weinbestand dar. 13
void ausgabeid(): Die Funktion gibt eine Liste des Weinbestands nach der Wein- ID sortiert aus. void ausgabejahrgang(): Die Funktion gibt eine Liste des Weinbestands nach Jahrgang sortiert aus. int getoldestwine(): Die Funktion liefert als Rückgabewert die Jahreszahl des ältesten verfügbaren Weins. 4. Erstellen Sie die Deklaration der Klasse weinbestandplus. Alle genannten Funktionen sollen von überall her aufrufbar sein. Mit welchem Mechanismus der objektorientierten Programmierung wird der Implementierungsaufwand erheblich reduziert? 5. Geben Sie den Aufwand der Funktionen sorte *getwine(int id), ausgabeid() und ausgabejahrgang in der O-Notation in Abhängigkeit vom Weinbestand n an. 6. Implementieren Sie die Funktion int getoldestwine(). 14