TU Kaiserslautern - AG Softwaretechnik

Werbung
TU Kaiserslautern
Prof. Dr. A. Poetzsch-Heffter
Dipl.-Inf. J.-M. Gaillourdet
Fachbereich Informatik
Dipl.-Inf. P. Michel
AG Softwaretechnik
Übungsblatt 11: Software-Entwicklung 1 (WS 2009/10)
Ausgabe: in der Woche vom 18.01. bis zum 22.01.
Abgabe: in der Woche vom 25.01. bis zum 29.01.
Abnahme: max. zwei Tage nach der Übung
Aufgabe 1 Heapsort (Präsenzaufgabe)
Sortieren Sie die Liste [23,1701,42,815,13] unter Verwendung des Heapsort-Algorithmus und geben Sie
dabei alle entstehenden Bäume an. Kennzeichnen Sie Bäume, welche die Heap-Eigenschaft verletzen.
Aufgabe 2 Listen (Präsenzaufgabe)
Implementieren Sie einen Verbunddatentyp für die Verwaltung von Listen von double-Werten in Java. Die
Liste soll mit Hilfe eines Arrays realisiert werden, d.h. alle Elemente der Liste werden in einem Array der
passenden Größe verwaltet. Folgende Verbunde und Prozeduren sollen Sie realisieren:
Programmkonstrukt
Beschreibung
class List { ... }
der zu entwerfende Verbunddatentyp
List create()
Erzeugt eine neue leere Liste.
void append(List l, double elem)
Hängt ein weiteres Element an die Liste an.
int size(List l)
Liefert die Anzahl der Listenelemente.
double get(List l, int index)
Liefert das Element der Liste an der Stelle index.
void remove(List l, int index)
Entfernt das Element mit index aus l. (Alle nachfolgenden Elemente ändern entsprechend ihren Index.)
boolean contains(List l, double elem)
Testet, ob elem in l enthalten ist.
Aufgabe 3 Listen und Komplexität (Einreichaufgabe)
a) Betrachten Sie eine Sequenz von n Aufrufen der Prozedur append mit vorheriger Erzeugung (create) der
leeren Liste. Leiten Sie die Aufwandsfunktion A(n) für diese Sequenz von Aufrufen her und bestimmen
Sie dann deren Komplexitätsklasse.
Als Kostenmaß verwenden Sie einheitliche Kosten von 1 für Zuweisungen, Vergleiche, arithmetischen
Operationen, Verbundkomponentenzugriffe und Feldzugriffe. Die Kosten für das Erzeugen eines neuen
Verbundobjekts sollen n+1 sein, wobei n die Anzahl der Komponenten des Verbunds sind. Im speziellen
Fall von Feldern sollen sie ebenfalls n + 1 betragen, wobei n hier die Länge des Feldes ist.
b) Wir betrachten nun eine mögliche Optimierung der implementierten Listen. Dazu erweitern wir den
Zustand des Listenverbunds um eine Komponente für die Anzahl der tatsächlich belegten Arrayzellen.
Jedesmal wenn das Array vergrößert werden muss, legen wir es größer an als nötig, wir verdoppeln es
in der Länge.
Implementieren Sie diese Optimierung und passen Sie die restlichen Prozeduren dementsprechend an.
Was sind die Vor- und Nachteile dieser Implementierung und in welchen Fällen sind diese besonders
relevant?
c) Führen Sie erneut eine Analyse der Zeitkomplexität durch, wie Sie es in a) für die einfache Implementierung getan haben. Gehen Sie hier allerdings vereinfachend davon aus, dass es ein k mit n = 2k + 1
gibt.
Hinweis: Geben Sie bei Prozeduren mit Fallunterscheidungen verschiedene Aufwandsfunktionen für die
einzelnen Fälle an und verwenden Sie an den Aufrufstellen dieser Prozeduren die entsprechend auf die
Parameter passende Variante.
Aufgabe 4 Speicherverbrauch (Einreichaufgabe)
In dieser Aufgabe betrachten wir eine Graphimplementierung, wo Knoten durch Verbunde dargestellt werden, die aus einem Wert und einem Feld von Knoten-Nachfolgern bestehen:
class Knoten {
int wert;
Knoten [] nachfolger ;
}
Wir wollen nun den Speicherbedarf von Graph-Geflechten bestimmen. Dabei dürfen Sie folgendes annehmen:
• Der Speicherverbrauch eines Werts vom Typ int ist 4, der eines Werts vom Typ boolean ist 1.
• Der Speicherverbrauch einer Referenz ist 4, egal ob Referenz auf einen Verbund oder ein Feld.
• Der Speicherverbrauch eines Feldes ist Länge mal Speicherverbrauch seines Elementtyps plus der
Konstanten 4, für den int-Eintrag, der die Länge des Feldes repräsentiert.
• Der Speicherverbrauch eines Verbunds ist die Summe des Speicherverbrauchs seiner Komponenten.
• Als Speicherverbrauch eines Geflechts – gegeben durch eine erste Referenz – bezeichnen wir die
Summe des Speicherverbrauchs aller Verbunde, die zu diesem Geflecht gehören, also von der Referenz aus erreichbar sind.
a) Schreiben Sie eine Prozedur int speicherVerbrauch(Knoten k), die rekursiv den Speicherverbrauch des
von k erreichbaren Graphen bestimmt. Welche Art von Graphen sind besonders problematisch?
Hinweis: Sie können in Ihrer Implementierung eine Listen-Implementierung aus Aufgabe 2 oder Aufgabe
3 verwenden, indem Sie den Element-Typ anpassen.
Laden Sie sich die Datei “TestMain.java” von der Vorlesungsseite und kopieren Sie die darin enthaltene main Prozedur in Ihr Programm. Sie testet Ihre speicherVerbrauch-Prozedur mit zwei Graphen, für
die als Ergebnis jeweils 336 herauskommen sollte.
b) Schätzen Sie die Zeitkomplexität Ihrer speicherVerbrauch-Prozedur für den schlechtesten Fall ab, in
Abhängigkeit von der Anzahl n der Knoten des Graphen.
c) Entwickeln Sie eine andere Lösung, die – bei gleicher Speicherkomplexität – eine bessere Zeitkomplexität besitzt, indem Sie Anpassungen am Knoten-Verbund vornehmen.
Aufgabe 5 Routenplanung (Einreichaufgabe)
Am Ende des Abschnitts 4.3 der Vorlesung wurde die Algorithmenentwicklung anhand eines Beispiels erläutert: Routenplanung in einer Stadt. Hier sehen Sie ein Beispiel für
einen Stadtplan als Graph mit gewichteten Kanten. Der
schnellste Weg vom Startknoten 1 zum Zielknoten 5 führt
über die Knoten 2 und 4.
Laden Sie sich zu dieser Aufgabe die Datei “Routenplanung.zip” von der Webseite der Vorlesung herunter. Sie
enthält u.a. eine Datei namens “Routenplanung.java”, in
die Sie bitte Ihre Lösungen einfügen. Es ist jeweils angegeben, wohin welche Lösung gehört.
2
1
2
3
6
3
2
4
1
4
5
9
a) In der Grafik auf der nächsten Seite sehen Sie die Repräsentation des obigen Stadtplans als Geflecht.
Erstellen Sie Verbundtypen gemäß dieser Graphik und Code um den Stadtplan als Geflecht anzulegen.
b) In der Vorlesung haben Sie gesehen, dass neben dem Graph zur Darstellung der Karte auch noch der
Rand des bearbeiteten Areals dargestellt werden muss. Code für den Rand ist in der heruntergeladenen Datei enthalten. Er ist nur als Class-Datei verfügbar. Sie können folgende Klasse und Methoden
verwenden.
class R.Rand { ... } // Verbund zur Darstellung eines Rand als Heap
Rand R. erzeugeRand () { ... }
boolean R.leer(Rand r) { ... }
Knoten R. naechsterKnoten (Rand r) { ... } // entferne den am naechsten gelegenen
// Knoten und liefere ihn zurueck
void R. entfernen (Rand r, Knoten k) { ... } // entferne den uebergebenen
// Knoten aus dem Rand
void R. einfuegen (Rand r, Knoten k) { ... } // fuege einen Knoten zum Rand hinzu
Implementieren Sie nun den in der Vorlesung entwickelten Algorithmus zum Finden der kürzesten Route. Testen Sie ihn am obigen Beispiel.
c) In dem heruntergeladenen Code ist auch ein Stadtplan enthalten. Testen Sie Ihre Routenplanung mit
diesem Anhand der Start- und Zielknoten-Paare, die in der Methode main stehen. Entfernen Sie dazu die
entsprechenden Kommentarzeichen. Eine Lösung ist jeweils darunter angegeben. Den gesamten Stadtplan finden Sie in der Datei Stadtplan.pdf.
Knoten
0
false
dist:
inB:
pred:
ausgehend:
1
ende:
gewicht:
Kante
2:
1:
0:
length:
Kante[]
9
3
ende:
gewicht:
Kante
ende:
gewicht:
Kante
Knoten
0
false
dist:
inB:
pred:
ausgehend:
4
6
4
Knoten
0:
length:
0
1
false
Kante[]
inB:
dist:
pred:
ausgehend:
2
Kante
ende:
gewicht:
Kante
ende:
gewicht:
1:
0:
length:
Kante[]
1
3
2
2
Knoten
0
false
dist:
inB:
pred:
ausgehend:
5
ende:
gewicht:
Kante
Kante
ende:
gewicht:
0:
length:
4
false
inB:
Kante[]
0
dist:
pred:
1
Knoten
ausgehend:
3
Herunterladen