4.¨Ubungsblatt - informatik.uni

Werbung
Praktische Informatik 3 WS 2007/08
4. Übungsblatt
Ausgabe: 10. 12. 2007
Abgabe: 7. 1. 2008
Berthold Hoffmann <hof>
Klaus Hartke <hartke>
Christian Maeder <maeder>
Dennis Walter <dw>
Diedrich Wolter <dwolter>
7 Mappen, Filter und Falter
5 Punkte
(i) Defininieren Sie die Funktion
any :: (a -> Bool) -> [a] -> Bool
any p [] = False
any p (h:t) = p h || any p t
als eine geeignete Kombination der Funktionen map, filter und foldr mit einer Gleichung der Form
“any p = ...”.
(ii) Definieren Sie map und filter mit Hilfe von foldr durch Gleichungen der Form “map f = foldr
...” bzw. “filter p = foldr ...”. Benutzen Sie dabei Lambda-Abstraktionen für die FunktionsParameter von foldr, anstatt mit where Hilfsfunktionen einzuführen. (Für diese Aufgabe müssen weder
eine Dokumentation noch Testfälle erstellt werden.)
8 Ein/Ausgabe per do-Notation
5 Punkte
Schreiben Sie ein interaktives Programm main in ein Modul Main, das kontinuierlich eine Eingabezeile
(von der Konsole) erfragt und zu jeder Zeile ihre Länge und die Anzahl der Wörter (auf der Konsole)
anzeigt. Die Eingabe von “quit”, “q” oder “exit” beendet den Dialog.
Vor Beendigung des Programms soll ein Protokoll der Sitzung, das alle Eingaben enthält, in eine Datei geschrieben werden. Der Dateiname wird dem Programm auf der Kommandozeile übergeben. (Ohne Kommandozeilenargument wird keine Protokolldatei angelegt; weitere Kommandozeilenargumente
können ignoriert werden.) Importieren Sie getArgs aus dem Modul System.Environment, um auf die
Kommandozeilenargumente zuzugreifen.
Hinweise: Ein sogenanntes “stand-alone”-Programm erhält man durch
runhaskell Main.hs hKommandozeilenargumente für maini
Wenn man eine literate Haskell Datei Main.lhs hat, kann man diese unter Unix (mit ‘chmod’) ausführbar
machen und als erste Zeile folgendes eintragen:
#!/usr/bin/env runhaskell
Danach ist dann folgender Aufruf möglich:
./Main.lhs hKommandozeilenargumente für maini
9 Oh, Du Fröhliche!
10 Punkte
Im Dorf des Weihnachtsmanns am Korvatunturi (Finnland) herrscht große Aufregung: Endlich ist der
neue Rentierschlitten eingetroffen, mit dem der Weihnachtsmann dank Sitzheizung, Teebecherwärmer
und nicht zuletzt GPS -programmierbarem Autopilot bequemer und schneller seine aufreibende Arbeit
verrichten kann. (Das Modell Joulupukki GT ist eine Sonderanfertigung aus einer Edelschmiede bei
Stuttgart.) Die Wichtel haben schon eine lange, lange Adressenliste all der besonders braven Kinder
(und StudentInnen, TutorInnen und DozentInnen) aufgestellt, die dieses Mal beschenkt werden sollen.
Nun gilt es nur noch, eine Route zu planen, die alle zu besuchenden Adressen auf einem möglichst
kurzen Weg erreicht. Da zum gelieferten Modell leider kein automatischer Routenplaner gehört, sollen
Sie den Wichteln helfen, eine möglichst kurze Route zu berechnen, mit der sie (die Wichtel nämlich) den
Autopiloten programmieren können. Bewährt sich Ihre Lösung, soll es in künftige Modelle des Herstellers
– auch in die stinkenden erdgebundenen – eingebaut werden, und Ihre Karriere wäre gesichert!
Abstrakt gesehen handelt es sich bei der Aufgabe um das bekannte Traveling Santa-Claus Problem (TSP ):
Der Weihnachtsmann soll einen optimalen Weg durch einen ungerichteten Graphen nehmen, dessen Knoten die zu besuchenden Stationen sind, und dessen Kanten mit der Entfernung zwischen den verbundenen
Stationen gewichtet sind. Wir suchen den kürzesten Hamilton-Zyklus, das ist der kürzeste Zyklus, der
alle Knoten besucht.
Das allgemeine Problem ist NP-vollständig. Hier können wir uns zu Nutze machen, dass der Graph
vollständig ist und die Dreiecksungleichung gilt: Die direkte Verbindung zwischen zwei Knoten ist immer
kürzer als jeder Weg über einen anderen Knoten. Dann lässt sich der kürzeste Zyklus mit guter Näherung
wie folgt finden: Man berechnet einen minimalen aufspannenden Baum des Graphen, zählt die Knoten
des Graphen nach der Präorder-Reihenfolge in diesem Baum auf und findet damit den Zyklus, der die
Knoten des Graphen in dieser Reihenfolge durchläuft. Dieser Weg ist zwar nicht optimal, aber höchstrens
doppelt so lang wie der optimale.
Der minimale aufspannende Baum wird mit dem Prim-Algorithmus berechnet: Dazu implementieren wir
eine Vorrangwarteschlange (priority queue) Pqueue a mit total geordneten Elementen (Ord a) und den
folgenden Operationen (mindestens):
• empty :: Pqueue a erzeugt die leere Schlange.
• isEmpty :: Pqueue a -> Bool testet, ob die Schlange leer ist.
• enqueue :: Ord a => Pqueue a -> a -> Pqueue a reiht ein Element in die Schlange ein.
• first::Ord a => Pqueue a -> a liefert das minimale Element einer Schlange.
• dequeue::Ord a => Pqueue a -> Pqueue a löscht das minimale Element einer Schlange.
• decreaseP::Ord a => Pqueue a -> (a->a) -> Pqueue a verringert die Prioritäten der Einträge
in der Schlange.
Grundsätzlich kann ein beliebiger Knoten als Wurzel des aufspannenden Baums genommen werden.
Hier sollte jedoch der Knoten ausgewählt werden, der den Beginn der Reise darstellt. Wir initialisieren
die Schlange mit allen Knoten des Graphen, wobei die Wurzel die Priorität 0 und alle anderen die
geringst mögliche Priorität (d. h. den maximalen Wert) bekommen. Aus der Schlange wird nun nach und
nach jeweils das minimale Element entnommen. Dies ist die nächste Station auf dem Weg, und für alle
verbleibenden Knoten wird die Priorität auf die Entfernung zum gerade entnommenen Knoten gesetzt,
falls die alte Priorität nicht schon kleiner war. Das Ergebnis ist eine Liste der Knoten des Graphen in
der gewünschten Reihenfolge. Da der Graph vollständig ist, ist dies genau der gesuchte Zyklus.
Der Algorithmus wird in drei Schritten implementiert:
1. Implementieren Sie ein Modul Pqueue, der den Typ Ord a => Pqueue a und die oben genannten
Operationen darauf exportiert.
Der Datentyp soll als geordnete Liste implementiert werden. Dies ist zwar im Allgemeinen nicht
optimal, weil enqueue und dequeue linearen Aufwand haben; in diesem Fall ist das nicht tragisch,
weil wir die Prioritäten oft ändern müssen. Das kleinste Element ist in einer geordneten Liste
immer vorne. Entscheidend ist, dass wir decreaseP jetzt effizient implementieren können: Wir
durchlaufen die Liste rekursiv und fügen jedes Element mit seiner neuen, geringeren oder mit seiner
alten Priorität so in die Liste ein, dass sie geordnet bleibt.
2. Ein Graph vom Typ Graph a b ist parametrisiert mit dem Knotentyp a und dem Typ b für die
Gewichtung der Kanten. Er kann mit einer Datenstruktur dargestellt werden, die aus einer Liste
der Knoten besteht und aus einer Funktion distance :: a -> a -> b, die für jeweils zwei Knoten
die Entfernung zwischen ihnen zurückgibt.
3. Die Hauptfunktion hat die Signatur
type Place
= String
type Latitude = (Int,Int,Int)
type Longitude = (Int,Int,Int)
type Position = (Latitude,Longitude)
type Stops
= [(Place, Position)]
xmasTour :: Stops -> (Stops,Double)
Die zu besuchenden Stationen werden als eine Liste von (Orts-) Namen und ihren Positionen eingegeben. Die Position besteht aus der geographischen Breite und Länge des Ortes, die in Grad,
Minuten und Sekunden angegeben wird. Minuten und Sekunden liegen im Bereich [0..59], und
der Grad im Bereich [-180..+180], wobei negative Werte Grade westlicher Länge bzw. südlicher
Breite angeben.
Um die Entfernung zwischen zwei Punkten zu berechnen, wandeln wir die Längen- und Breitenangaben zunächst in Radian um. Für g Grad, m Minuten und s Sekunden ergibt sich der Radian
als
m
s π
grad = g +
+
60 3600 180
Für die Berechnung von Entfernungen auf der Erdoberfläche nehmen näherungsweise wir an, die
= 6367 km. Die Entfernung d zwischen zwei Punkten
Erde sei eine Kugel mit Radius R = 40.000,3
2π
mit den Breiten δ1 , δ2 und den Längen φ1 , φ2 (gegeben in Radian) wird mit der Semiversusformel
berechnet:
s
!
−1
2 δ2 − δ1
2 φ2 − φ1
sin
+ cos δ1 cos δ2 sin
d = 2R sin
2
2
Die Funktion xmasTour liefert ein Paar, bestehend aus den Stationen der Tour in der richtigen
Reihenfolge, und den Kilometern, die zurückgelegt werden müssen, bis der Rentierschlitten wieder
in der Garage im Weihnachtsdorf steht. Die steht an der Position (+68◦ 03′ 14′′ , +29◦ , 25′ , 30′′ ). Dort
beginnt und endet die Reise.
(Einige Beispieltouren für Testzwecke finden sich im WWW neben dem Link auf diesen Aufgabentext.)
Frohe Weihnachten und einen guten Start ins Neue Jahr!
Dieses ist Version 0.99 vom 5. Dezember 2007.
Herunterladen