Teillösungen zum 13. und letzten Aufgabenblatt zur Vorlesung Informatik A (Autor: Florian Schmidt) 1. Reguläre Sprachen Aus der Vorlesung ist bekannt, dass zu jeder regulären Sprache L ein DFA existiert, der genau alle w ∈ L akzeptiert. Umgekehrt ist die Menge aller Wörter, die ein DFA erkennt, immer eine reguläre Sprache. Zu den gegebenen Sprachen L1 und L2 existieren also zwei endliche deterministische Automaten A1 und A2 . Es recht zu zeigen, dass zu Lc1 , L1 \ L2 , L1 ∩ L2 und Lrev endliche Automaten existieren, die die jeweilige Sprache erkennen. (a) Sei A1 = (Σ, Q, s0 , δ, F ) der DFA für L1 und sei Ac1 der DFA für die Komplementärsprache Lc1 gegeben durch (Σ, Q, s0 , δ, F c ). In Ac sind genau alle Zustände akzeptierend , die in A1 nicht akzeptierend sind und umgekehrt, mithin F c = Q\F . Für L1 ∩L2 lässt sich ein Automat B = (Σ, Q, s0 , δ, F ) aus A1 = (Σ1 , Q1 , s1 , δ1 , F1 ) und A2 = (Σ2 , Q2 , s2 , δ2 , F2 ) wie folgt konstruieren: Die neue Zustandsmenge besteht aus allen Kombinationen der Zustände von A1 und A2 , also Q = Q1 × Q2 = {(p, q) | p ∈ Q1 , q ∈ Q2 }. Wenn einer der neuen Zustände aus einem akzeptierenden Zustand von A1 und aus einem akzeptierenden Zustand von A2 besteht, soll er akzeptierend im neuen Automaten B sein: F = (F1 × F2 ). Startzustand von B ist der Zustand, der aus den beiden Startzuständen von A1 und A2 gebildet wurde: s0 = (s1 , s2 ) . Die Übergangsfunktion δ(σ, (p, q)) = (δ1 (σ, p), δ2 (σ, q)) simultiert die gleichzeitige Ausführung von A1 und A2 . B akzeptiert dabei nur, wenn beide Automaten A1 und A2 akzeptieren würden. Somit erkennt B genau die Wörter aus L1 ∩ L2 . Alternativ: L1 ∩ L2 = (Lc1 ∪ Lc2 )c . Da Sprachen Mengen von Wörtern sind, lässt sich L1 \ L2 als L1 ∩ LC 2 ausdrücken. Reguläre Sprachen sind wie gezeigt unter Komplement und Durchschnitt abgeschlossen. Also ist auch L1 \ L2 wieder eine reguläre Sprache. (b) Sei A der Automat, welcher die reguläre Sprache L erkennt. In Arev sollen alle Zustände aus A enthalten sein, wobei die Übergänge jeweils umgedreht wurden. Ist δ(σ, p) = q, so sei p ∈ δrev (σ, q). Der Startzustand s0 aus A ist einziger akzeptierender Zustand in Arev . In Arev kommt ein neuer Zustand sstart hinzu, welcher mit allen akzeptierenden Zuständen aus A mittels -Übergang verbunden ist: δrev (, sstart ) = F . Der neue Zustand sstart soll Startzustand in Arev sein. Arev akzeptiert also genau dann, wenn einer der Wege von einem akzeptierenden Zustand in A zum Startzustand in A führt. Das sind genau alle Wörter in Lrev . Arev ist ein NFA und kann demnach in einen äquivalenten DFA umgewandelt werden. Somit ist Lrev regulär. 2. Haskell I (a) Alle Zahlen, die um eins erhöht größer als 0 sind, müssen vorher größer als −1 gewesen sein. Somit ist sec2 der Asudruck >(-1). Weil alle Zahlen um eins erhöht werden müssen, ist sec1 der Ausdruck +1. (b) Der Wert ist [(n,2^n) | n<-[0..]]. [(0,1),(1,2),(2,4),(3,8),(4,16),...] bzw. (c) drop lässt die ersten n Elemente einer Liste weg und drop lässt alle Elemente nach dem n-ten Element weg. Somit ist das Ergebnis die Liste [" i","nfa123"]. 3. Haskell II, Algebraische Typen (a) data StammBaum = Nil | Knoten (String, Int) StammBaum StammBaum deriving Show (b) durchSchnitt :: StammBaum -> Float durchSchnitt xs = fromIntegral (sum (alter xs)) / fromIntegral (length (alter xs) where alter :: StammBaum -> [Int] -- Extrahiert die Liste aller Alterszahlen alter Nil = [] alter (Knoten (a,b) l r) = [b] ++ alter l ++ alter r (c) anfrageName :: [StammBaum] -> String -> [StammBaum] anfrageName xs name = concat(map (sucheNamen name) xs) where -- Sucht alle Teilbäume, die namen als Wurzel haben sucheNamen :: String -> StammBaum -> [StammBaum] sucheNamen name Nil = [] sucheNamen name (x@(Knoten (n,a) l r)) | n == name = [x] ++ sucheNamen name l ++ sucheNamen name r | otherwise = sucheNamen name l ++ sucheNamen name r 4. Blockcode (a) Für Blockcodelänge 15 gibt es 215 Codewörter über dem Alphabeth {0, 1}. Der betrachtete Code soll 1-fehlerkorrigierend sein. Für ein Codewort z.B. w = 010011010110110 sollen also alle Codewörter, welche genau einen Fehler aufweisen, ebenfalls auf w decodiert werden. Sie stehen als Codewörter nicht mehr zur Verfügung. Für w gibt es genau 15 Codewörter, welche genau einen Fehler aufweisen: 110011010110110, 000011010110110, 011011010110110 usw. Jedes Codewort verbraucht also 1 + 15 = 24 mögliche Codewörter. Das bedeutet, dass es höchstens 215 = 211 Codewörter geben kann. 24 (b) Gegeben seien 128 verschiedene Zeichen mit relativen Häufigkeiten ihres Auftretens. Dabei sei die maximale auftretende Häufigkeit eines Zeichens kleiner als das Doppelte der minimalen Häufigkeit. Beweisen Sie, dass dann ein Blockcode (was ist dessen Blocklänge ?) schon ein optimaler Präfixcode ist. Lösung: Der Blockcode hat Länge 7 wegen 27 = 128. Optimale Präfixcodes werden von der Huffman-Codierung erzeugt, wir überlegen uns, dass dabei der volle balancierte Binärbaum mit Tiefe 7 entsteht. Huffman geht greedy vor. Seien p1 , p2 , . . . , pn die aufsteigend sortierten Häufigkeiten. Nach Voraussetzung werden zuerst p1 mit p2 vereinigt, die Liste der aufsteigend sortierten Häufigkeiten danach ist p3 , p4 , . . . pn , p1 + p2 . Nach n/2 = 64 Schritten sieht die Liste wie folgt aus p1 + p2 , p3 + p4 , . . . , pn−1 + pn . Man beachte, dass für diese halb so lange Liste wieder gilt 2(p1 + p2 ) > (pn−1 + pn ) und man wiederholt das Ganze. (Formal für Listen der Länge 2k mit Induktion über k.)