Aufgabensammlung zur Einführung in die Programmierung Inhaltsverzeichnis 1 Logik, Mengenlehre, Funktionen 2 2 URM-Programmierung 5 3 Hoare-Kalkül 8 4 Komplexität 13 5 Abstrakte Datentypen 17 6 Formale Sprachen, BNF, EBNF 23 7 Zahldarstellung 29 1 1. 2 1 Logik, Mengenlehre, Funktionen Aufgabe 1.1 a) Erstellen Sie eine Wahrheitstafel für (A ⇒ B) ⇒ B b) Erstellen Sie eine Wahrheitstafel für (A ∧ ¬B) ∨ (¬A ∧ B) c) Erstellen Sie eine Wahrheitstafel für ((A ⇒ B) ∧ (B ⇒ C)) ⇒ (A ⇒ C) Lösung zu 1.1 a) Wahrheitstafel: A B A ⇒ B (A ⇒ B) ⇒ B 0 0 1 0 0 1 1 1 1 0 0 1 1 1 1 1 b) (A ∧ ¬B) ∨ (¬A ∧ B) stellt das ausschließende oder“ (exclusive or, XOR) dar: ” A B A ∧ ¬B ¬A ∧ B (A ∧ ¬B) ∨ (¬A ∧ B) 0 0 0 0 0 0 1 0 1 1 1 0 1 0 1 1 1 0 0 0 c) Hier sind einige Zwischenschritte sinnvoll: A B C A ⇒ B B ⇒ C (A ⇒ B) ∧ (B ⇒ C) A ⇒ C ((A ⇒ B)∧(B ⇒ C)) ⇒ (A ⇒ C) 0 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 0 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 Aufgabe 1.2 Bilden Sie die logischen Verneinungen von a) b) c) d) e) Wenn es regnet, dann wird die Straße nass (a ≤ b) ∨ (a = 0) x>0∧y <0 Alle Vielfachen von 10 sind durch 2 und durch 5 teilbar p(x) = T RU E, wobei p(x) eine Funktion sei, die einen boole’schen Wert liefert (also ein Prädikat). Können Sie den Ausdruck verkürzen? Lösung zu 1.2 (Wir benutzen die Schreibweise ¬X für die Negation der logischen Aussage X statt X.) a) Wenn es regnet, dann wird die Straße nass ist gleichbedeutend zu es regnet nicht oder die Straße ist nass. Die Verneinung hiervon ist es regnet und die Straße ist nicht nass. Alle anderen Kombinationen (z.B. es regnet nicht und die Straße ist nass) sind keine logische Negation der obigen Aussage. b) Nach der ersten de Morgansche Regel ¬(A ∨ B) = ¬A ∧ ¬B ist ¬((a ≤ b) ∨ (a = 0)) ≡ ¬(a ≤ b) ∧ ¬(a = 0) ≡ (a > b) ∧ (a 6= 0) c) Hier benutzen wir die zweite de Morgansche Regel ¬(A ∧ B) = ¬A ∨ ¬B und erhalten ¬((x > 0) ∧ (y < 0)) ≡ ¬(x > 0) ∨ ¬(y < 0) ≡ (x ≤ 0) ∨ (y ≥ 0) 1. 3 d) Formal lautet die Aussage: ∀x ∈ N : x ist Vielfaches von 10 → 2|x ∧ 5|x. Die Negation davon ist: ∃x ∈ N : x ist Vielfaches von 10 9 (2|x ∧ 5|x) Dazu ist äquivalent ∃x ∈ N : x ist Vielfaches von 10 ∧ (2 ∤ x ∨ 5 ∤ x). In Worten: Es gibt ein Vielfaches von 10, das nicht durch 2 oder nicht durch 5 teilbar ist. e) Der Ausdruck p(x) = T RU E ist selber schon äquivalent zum Ausdruck p(x), die Negation erhalten wir also einfach durch ¬p(x). Aufgabe 1.3 Paul sagt: Max lügt.“ ” Max sagt: Otto lügt.“ ” Otto sagt: Max und Paul lügen.“ ” Wer lügt hier nun wirklich, wer sagt die Wahrheit? Lösung zu 1.3 Wenn Paul die Wahrheit sagt, dann muß Max lügen, also Otto die Wahrheit sagen. Dies ist aber ein Widerspruch zu der Annahme, dass Paul nicht lügt. Also lügt Paul, damit sagt Max die Wahrheit und Otto lügt. Man kann die Aufgabe auch formal lösen: Wir bezeichnen mit P die Aussage Paul sagt die Wahrheit“und mit ¬P die Aussage Paul lügt“. Entsprechend ” ” verwenden wir O, ¬O, M, ¬M . Dann ist der erste Satz gleichbedeutend mit P ↔ ¬M , der zweite Satz entspricht M ↔ ¬O und die dritte Aussage ist O ↔ (¬M ∧ ¬P ). Die Aufgabe besteht nun darin, eine Belegung der Variablen“P, M und O mit Wahrheitswerten wahr, falsch ∈ B ” zu finden, sodass alle drei Aussagen den Wahrheitswert wahr haben. Dazu kann man eine Tabelle erstellen: (Wir verwenden f für falsch und w für wahr) P f f f f w w w w M f f w w f f w w O f w f w f w f w P ↔ ¬M f f w w w w f f M ↔ ¬O f w w f f w w f O ↔ (¬M ∧ ¬P ) f w w f w f w f Nur die eingerahmte Zeile erfüllt alle drei Aussagen. Die Wahrheitstabelle für ↔ ist dabei: A w w f f B w f w f A↔B w f f w Aufgabe 1.4 Betrachten Sie die Mengen M1 = {3, 5, 7} und M2 = {4, 5, 6} . Bilden Sie a) b) c) d) e) die Potenzmengen, das kartesische Produkt M1 × M2 , den Schnitt, die Vereinigung und die Differenzmengen M1 − M2 und M2 − M1 . 1. 4 Finden Sie Beispiele für a) eine partielle Funktion von M1 nach M2 , b) eine totale Funktion von M1 nach M2 , c) eine Relation (die nicht zugleich Funktion ist) auf M1 × M2 . Lösung zu 1.4 a) P(M1 ) = {∅, {3}, {5}, {7}, {3, 5}, {3, 7}, {5, 7}, {3, 5, 7}} P(M2 ) = {∅, {4}, {5}, {6}, {4, 5}, {4, 6}, {5, 6}, {4, 5, 6}} M1 × M2 = {(3, 4), (3, 5), (3, 6), (5, 4), (5, 5), (5, 6), (7, 4), (7, 5), (7, 6)} M1 ∩ M2 = {5} M1 ∪ M2 = {3, 4, 5, 6, 7} M1 − M2 = {3, 7} M2 − M1 = {4, 6} b) {(3, 4), (5, 4)} ist eine partielle Funktion, {(3, 4), (5, 4)(7, 5)} ist eine totale Funktion und {(3, 4), (5, 4)(5, 6)} ist eine Relation, aber keine Funktion. Aufgabe 1.5 Betrachten Sie f : Z × Z → Z, f (x, y) = 2x + 3y und g : Z → Z, g(x) = 5x − 1. a) Berechnen Sie f (g(x), x) für x = 1. b) Berechnen Sie f (f (x, y), g(y)) für x = 1 und y = 2. c) Bestimmen Sie die Vorschrift der zusammengesetzten Funktion g ◦ f . Lösung zu 1.5 a) f (g(1), 1) = f ((5 ∗ 1 − 1), 1) = f (4, 1) = 2 ∗ 4 + 3 ∗ 1 = 11 b) f (f (1, 2), g(2)) = f (2 ∗ 1 + 3 ∗ 2, 5 ∗ 2 − 1) = f (8, 9) = 2 ∗ 8 + 3 ∗ 9 = 43 c) g ◦ f : Z × Z → Z, mit g(f (x, y)) = g(2x + 3y) = 5(2x + 3y) − 1 = 10x + 15y − 1. Aufgabe 1.6 Betrachten Sie die Funktionen f1 : N → N, f2 : N2 → N, g1 : N → N und g2 : N → N definiert durch f1 (x) = 1 falls x = 0 2 + f1 (x − 1)) falls x > 0 f2 (x, y) = f1 (x) + 2f1 (y) g1 (x) = 2x − 1 und g2 (x) = 3 − x. Bestimmen Sie die Werte f1 (0), f1 (1), . . . f1 (5) und f2 (g1 (x), g2 (x)) für x = 1 . . . 3. Lösung zu 1.6 f1 (0) = 1 f1 (1) = 2 + f1 (0) = 2 + 1 = 3 f1 (2) = 2 + f1 (1) = 2 + 3 = 5 f1 (3) = 2 + f1 (2) = 2 + 5 = 7 f1 (4) = 2 + f1 (3) = 2 + 7 = 9 f1 (5) = 2 + f1 (4) = 2 + 9 = 11 Für f2 (g1 (x), g2 (x)) = f2 (2x − 1, 3 − x) ergibt sich für x = 1 : f2 (2 ∗ 1 − 1, 3 − 1) = f2 (1, 2) = f1 (1) + 2f1 (2) = 3 + 2 ∗ 5 = 13 x = 2 : f2 (2 ∗ 2 − 1, 3 − 2) = f2 (3, 1) = f1 (3) + 2f1 (1) = 7 + 2 ∗ 3 = 13 x = 3 : f2 (2 ∗ 3 − 1, 3 − 3) = f2 (5, 0) = f1 (5) + 2f1 (0) = 11 + 2 ∗ 1 = 13 2. 5 2 URM-Programmierung Aufgabe 2.1 a) Schreiben Sie ein URM-Programm, das testet, ob die Eingabe gleich 0 ist, dh. bei Eingabe einer 0 soll 1 (für true) ausgegeben werden, bei allen anderen Eingaben soll 0 (für false) ausgegeben werden. Genauer: Das Programm soll die Funktion [P ] : N0 → N0 [P ](x) = 0 falls x > 0 1 falls x = 0 berechnen. Beachten Sie, dass nicht nur der Befehlsteil, sondern auch Eingabe- und Ausgabevereinbarung angegeben werden. b) Berechnen Sie für Ihr Programm formal (mit Hilfe der iterierten Zustandstransformation) den Wert [P ](2). Lösung zu 2.1 a) Das Programm P = in (r1 ); b; out (r2 ) mit b = 1 : r2 ← 0 2 : if r1 = 0 goto 4 3 : goto 5 4 : r2 ← r2 + 1 5: berechnet die Funktion [P ] : N0 → N0 [P ](x) = 0 falls x > 0 1 falls x = 0 b) Wir müssen [P ](2) = [ out (r2 )] ◦ [b] ◦ [ in (r1 )](2) bestimmen. Wir berechnen also zunächst aus der Eingabevereinbarung [ in (r1 )] und dem Eingabewert 2 den Anfangszustand z0 , wenden daruf die iterierte Zustandstransformation [b] an und abschließend auf den erreichten Endzustand zs die Ausgabevereinbarung [ out (r2 )]. Der Anfangszustand ist z0 = [ in (r1 )](2) = (1, 2, 0, . . .) oder genauer: z0 (0) = 1, z0 (1) = 2, z0 (j) = 0 für j ≥ 2. In Worten: der Befehlszähler (Register 0) wird auf 1 gesetzt, Register 1 erhält den Eingabewert 2 und alle anderen Register den Wert 0. Auf z0 wenden wir nun die iterierte Zustandstransformation an: δ(b)(1, 2, 0, . . .) = [r2 ← 0] (1, 2, 0, . . .) = (2, 2, 0, . . .) δ(b)(2, 2, 0, . . .) = [ if r1 = 0 goto 4] (2, 2, 0, . . .) = (3, 2, 0, . . .) δ(b)(3, 2, 0, . . .) = [ goto 5 ] (3, 2, 0, . . .) = (5, 2, 0, . . .) = z3 Wir haben also [b](z0 ) = z3 = (5, 2, 0, . . .). Auf den Endzustand z3 wird jetzt die Ausgabevereinbarung angewendet und wir erhalten [ out (r2 )](z3 ) = [ out (r2 )](5, 2, 0, . . .) = 0 Insgesamt haben wir also [P ](2) = [ out (r2 )] ◦ [b] ◦ [ in (r1 )](2) = [ out (r2 )] ◦ [b](1, 2, 0, . . .) = [ out (r2 )](5, 2, 0, . . .) =0 2. 6 Aufgabe 2.2 a) Schreiben Sie ein URM-Programm, das zwei Zahlen auf Gleichheit testet: Bei Gleichheit soll 1, bei Ungleichheit soll 0 ausgegeben werden. b) Berechnen Sie [P ](2, 1) und [P ](0, 0) für Ihr Programm aus a). Lösung zu 2.2 a) P = in (r1 , r2 ); b; out (r3 ) mit b = 1: r3 ← 0 2: if r1 = 0 goto 7 3: if r2 = 0 goto 10 4: r1 ← r1 −̇1 5: r2 ← r2 −̇1 6: goto 2 7: if r2 = 0 goto 9 8: goto 10 9: r3 ← r3 + 1 Erläuterung: In der Schleife 2 - 6 werden beide Register parallel heruntergezählt, bis mindestens eines bei 0 angelangt ist. In den Anweisungen 7 und 8 werden die verschiedenen möglichen Fälle getestet: Anweisung 9 wird nur erreicht, falls r1 = 0 und r2 = 0, in diesem Fall erhält Register 3 den Wert 1. In allen anderen Fällen (r1 6= 0 ∧ r2 = 0 oder r1 = 0 ∧ r2 6= 0) wird Anweisung 9 übersprungen und Register 3 behält den Wert 0. b) Um [P ](2, 1) zu berechnen, bestimmt man zunächst den Wert der Eingabefunktion [ in (r1 , r2 )], wendet auf den dadurch gegebenen Zustand z0 die iterierte Zustandstransformation [b] an und auf den dadurch erreichten Endzustand zs die Ausgabefunktion [ out (r3 )]. Wir bilden also [P ](2, 1) = [ out (r3 )] ◦ [b] ◦ [ in (r1 , r2 )](2, 1) = [ out (r3 )] ◦ [b](z0 ) und erhalten z0 = [ in (r1 , r2 )] = (1, 2, 1, 0, . . .) , oder genauer: z0 (0) = 1, z0 (1) = 2, z0 (2) = 1, z0 (j) = 0 für j ≥ 3. Auf diesen Zustand z0 wird nun wiederholt die Einzelschrittfunktion angewendet: δ(b)(1, 2, 1, 0, . . .) = [r3 ← 0] (1, 2, 1, 0, . . .) = (2, 2, 1, 0, . . .) δ(b)(2, 2, 1, 0, . . .) = [ if r1 = 0 goto 7] (2, 2, 1, 0, . . .) = (3, 2, 1, 0, . . .) δ(b)(3, 2, 1, 0, . . .) = [ if r2 = 0 goto 8] (3, 2, 1, 0, . . .) = (4, 2, 1, 0, . . .) δ(b)(4, 2, 1, 0, . . .) = [r1 ← r1 −̇1] (4, 2, 1, 0, . . .) = (5, 1, 1, 0, . . .) δ(b)(5, 1, 1, 0, . . .) = [r2 ← r2 −̇1] (5, 1, 1, 0, . . .) = (6, 1, 0, 0, . . .) δ(b)(6, 1, 1, 0, . . .) = [ goto 2] (6, 1, 0, 0, . . .) = (2, 1, 0, 0, . . .) δ(b)(2, 1, 0, 0, . . .) = [ if r1 = 0 goto 7] (2, 1, 0, 0, . . .) = (3, 1, 0, 0, . . .) δ(b)(3, 1, 1, 0, . . .) = [ if r2 = 0 goto 8] (3, 1, 0, 0, . . .) = (8, 1, 0, 0, . . .) δ(b)(8, 1, 0, 0, . . .) = [ goto 10] (8, 1, 0, 0, . . .) = (10, 1, 0, 0, . . .) wir haben also zs = (10, 1, 0, 0, . . .). Nun noch die Ausgabefunktion [ out (r3 )] und wir erhalten [P ](2, 1) = [ out (r3 )](10, 1, 0, 0, . . .) = 0. Eingabe der Werte 2 und 1 in die Register r1 bzw. r2 liefert die Ausgabe 0 (= Inhalt des Registers r3 im Endzustand). Der Ausgabewert des Programms für die Eingabe (0, 0) berechnet sich aus [P ](0, 0) = [ out (r3 )] ◦ [b] ◦ [ in (r1 , r2 )](0, 0) = [ out (r3 )] ◦ [b](1, 0, 0, 0, . . .) mit δ(b)(1, 0, 0, 0, . . .) = [r3 ← 0] (1, 0, 0, 0, . . .) = (2, 0, 0, 0, . . .) δ(b)(2, 0, 0, 0, . . .) = [ if r1 = 0 goto 7] (2, 0, 0, 0, . . .) = (7, 0, 0, 0, . . .) δ(b)(7, 0, 0, 0, . . .) = [ if r2 = 0 goto 9] (7, 0, 0, 0, . . .) = (9, 0, 0, 0, . . .) δ(b)(9, 0, 0, 0, . . .) = [r3 ← r3 + 1] (9, 0, 0, 0, . . .) = (10, 0, 0, 1, 0 . . .) und also [P ](0, 0) = [ out (r3 )](10, 0, 0, 1, 0 . . .) = 1. 2. 7 Aufgabe 2.3 Schreiben Sie ein URM-Programm, das den Abstand f (x, y) = |x − y| berechnet. Informell ausgedrückt: Der Abstand ist größere minus kleinere “. ” Aber Achtung! Es ist hier nicht sinnvoll, zuerst das Maximum und Minimum zu bestimmen, um dann die Differenz zu berechnen. Lösung zu 2.3 Idee: beide Register werden parallel heruntergezählt. Wenn eines von beiden (das kleinere) auf 0 angelangt ist, enthält das andere gerade den Abstand. (Zeilen1 bis 5) Da man aber die Ausgabe nicht von einer Bedingung abhängig machen kann ( wenn r1 = 0 dann Ausgabe r2 “oder ” umgekehrt), muss man ein Ausgaberegister definieren, auf das der Inhalt des Ergebnisses kopiert wird. (Zeilen 6 9, wenn r1 die kleinere Zahl enthielt und Zeile 10 - 13, wenn r2 kleiner war.) P = in (r1 , r2 ); b; out (r3 ) mit b = 1 : if r1 = 0 goto 6 2 : if r2 = 0 goto 10 3 : r1 ← r1 −̇1 4 : r2 ← r2 −̇1 5 : goto 1 6 : if r2 = 0 goto 14 7 : r2 ← r2 −̇1 8 : r3 ← r3 + 1 9 : goto 6 10: if r2 = 0 goto 14 11: r1 ← r1 −̇1 12: r3 ← r3 + 1 13: goto 10 3. 8 3 Hoare-Kalkül Aufgabe 3.1 Beweisen Sie mit den Regeln des Hoare’schen Kalküls, dass die Variable a nach Ausführung der Anweisung a := 5 immer (dh. bei leerer Vorbedingung) den Wert 5 hat. Lösung zu 3.1 Wir wollen zeigen, dass die Formel { (1) } a := 5 {a = 5} gültig ist. Dazu verwenden wir Axiom 2 (Wertzuweisung) {} {p[u/t]} u := t {p} des Hoare’schen Kalküls. Da es sich um ein Axiom handelt, brauchen wir keine Voraussetzungen zu überprüfen (erkennbar daran, dass die Prämisse leer ist). Die Konklusion ist also richtig für jede Nachbedingung {p}, wenn die Vorbedingung dadurch aus p hervorgeht, dass man jedes Vorkommen der Variable u (bei uns also a) durch den Ausdruck t (bei uns also 5) ersetzt. Unsere Nachbedingung ist {a = 5}. Um die Vorbedingung zu erhalten, müssen wir im Ausdruck {a = 5} jedes Vorkommen der Variable a durch den Ausdruck 5 ersetzen. Wir erhalten als Vorbedingung {5 = 5} und können aufgrund des Axioms schließen, dass die Korrektheitsformel {5 = 5} a := 5 (2) {a = 5} gilt. In Worten sagt uns Formel (2) folgendes: Wenn vor Ausführung der Anweisung a := 5 die Bedingung {5 = 5} wahr ist, dann ist nach der Wertzuweisung die Aussage {a = 5} richtig. Oder umgekehrt: Um zu garantieren, dass nach der Wertzuweisung a := 5 richtig ist, muss vor der Ausführung {5 = 5} erfüllt sein. Die Aussage {5 = 5} ist aber immer wahr! Formal schreibt sich das so: {} ⇒ {5 = 5} Wir können also Regel 6 (Konsequenzregel) anwenden und erhalten {} ⇒ {5 = 5}, {5 = 5} a := 5 {a = 5} {} a := 5 {a = 5} Beide Prämissen sind erfüllt, also gilt die Konklusion und (1) ist bewiesen. Aufgabe 3.2 Beweisen Sie, dass nach Ausführung der Anweisung if x ≥ y then a := x − y else a := y − x die Variable a auf jeden Fall (also bei leerer Vorbedingung) einen Wert ≥ 0 hat. Lösung zu 3.2 Die Behauptung enthät zwei Teilaussagen: • Nach der Anweisung if x ≥ y then a := x − y else a := y − x ist a ≥ 0, also soll die Nachbedingung {a ≥ 0} gelten und • das gilt immer, also unter leerer Vorbedingung: { } 3. 9 Wir haben also die Gültigkeit der Formel (3) { } if x ≥ y then a := x − y else a := y − x {a ≥ 0} zu zeigen. Dazu wollen wir Regel 4 (Selektionsregel) anwenden: {p ∧ B}S1 {q}, {p ∧ ¬B}S2 {q} {p} if B then S1 else S2 end {q} In unserem Fall ist p: { } B: x≥y q : {a ≥ 0} S1 : a := x − y S2 : a := y − x. und Um eine Regel, die kein Axiom ist, anwenden zu können, muss man nachweisen, dass die Prämissen der Regel erfüllt sind, hier also, dass die beiden Formeln {p ∧ B}S1 {q} und {p ∧ ¬B}S2 {q} gelten. Wir setzen unsere Ausdrücke für p, B, S1 , S2 und q ein und erhalten als Zwischenziel: zeige die Gültigkeit von (4) {{ } ∧ (x ≥ y)} a := x − y {a ≥ 0} und (5) {{ } ∧ ¬(x ≥ y)} a := y − x {a ≥ 0} Dies wird mit Axiom 2 erreicht: Danach gilt nämlich {x − y ≥ 0} a := x − y {a ≥ 0} und wegen (x ≥ y) =⇒ (x − y ≥ 0) {x ≥ y} a := x − y {a ≥ 0} also ist (4) eine korrekte Formel. und Regel 6 folgt Ebenso gilt nach Axiom 2: {y − x ≥ 0} a := y − x {a ≥ 0} und wegen (x < y) =⇒ (y − x > 0) =⇒ (y − x ≥ 0) folgt {x < y} a := y − x {a ≥ 0} also ist auch (5) richtig. und Regel 6 Aufgabe 3.3 Betrachten Sie das Programm: x := a y := b z := 0 w h i l e ( x > 0 and y > 0 ) do x = x−1 y = y−1 en d w h ile a) Zeigen Sie, dass (z + x = a) ∧ (z + y = b) ∧ (a ≥ 0) ∧ (b ≥ 0) eine Invariante für die while-Schleife ist. b) Beweisen Sie damit, dass das Programm unter der Vorbedingung (a ≥ 0) ∧ (b ≥ 0) korrekt das Minimum von a und b berechnet, dass also die Nachbedingung ((z = a) ∧ (a ≤ b)) ∨ ((z = b) ∧ (b ≤ a)) gilt. 3. 10 Lösung zu 3.3 a) Behauptung: I : (z + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0) ist eine Invariante für die whileSchleife. Es muß also gezeigt werden, dass {I ∧ B}S{I} gilt mit I wie oben, B : (x > 0 ∧ y > 0) und S : x := x − 1; y := y − 1; z := z + 1. Wir schließen rückwärts: Nach Axiom 2 gilt {(z + 1 + x = a) ∧ (z + 1 + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0)}z := z + 1 {(z + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0)} außerdem (nocheinmal Axiom 2 ) {(z + 1 + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y − 1 ≥ 0)}y := y − 1 {(z + 1 + x = a) ∧ (z + 1 + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0)} und noch einmal: {(z + x = a) ∧ (z + y = b) ∧ (x − 1 ≥ 0) ∧ (y − 1 ≥ 0)}x := x − 1 {(z + 1 + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y − 1 ≥ 0)} also nach Regel 3: {(z + x = a) ∧ (z + y = b) ∧ (x − 1 ≥ 0) ∧ (y − 1 ≥ 0)}x := x − 1; y := y − 1; z := z + 1 {(z + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0)} Nun gilt aber {I ∧ B} = {x + z = a ∧ y + z = b ∧ x ≥ 0 ∧ y ≥ 0 ∧ x > 0 ∧ y > 0} =⇒ {z + x = a ∧ y + z = b ∧ x − 1 ≥ 0 ∧ y − 1 ≥ 0} und deshalb nach Regel 6 die Behauptung. b) Wir haben eine Schleifeninvariante I gefunden, damit läßt sich Regel 5 anwenden und es gilt: {I} while . . . {I ∧ ¬B} also (z + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0) while . . . {x + z = a ∧ y + z = b ∧ x ≥ 0 ∧ y ≥ 0 ∧ (x ≤ 0 ∨ y ≤ 0)} Es gilt aber (x + z = a ∧ y + z = b ∧ x ≥ 0 ∧ y ≥ 0 ∧ (x ≤ 0 ∨ y ≤ 0)) =⇒ (x + z = a ∧ y + z = b ∧ (x = 0 ∨ y = 0)) =⇒ (z = a ∧ a ≤ b) ∨ (z = b ∧ b ≤ a) und es läßt sich wieder Regel 6 anwenden. Man hat also {I} while . . . {(z = a ∧ a ≤ b) ∨ (z = b ∧ b ≤ a)} Nun bleibt noch zu zeigen: {a ≥ 0 ∧ b ≥ 0}x := a; y := b; z := 0{I}. Dies erreicht man aber leicht durch Regel 2, Regel 3 und Regel 6 (vorwärts hingeschrieben, aber rückwärts zu lesen): {a ≥ 0 ∧ b ≥ 0} =⇒ {a = a ∧ b = b ∧ a ≥ 0 ∧ b ≥ 0} {a = a ∧ b = b ∧ a ≥ 0 ∧ b ≥ 0}x := a{x = a ∧ b = b ∧ x ≥ 0 ∧ b ≥ 0} {x = a ∧ b = b ∧ x ≥ 0 ∧ b ≥ 0}y := b{x = a ∧ y = b ∧ x ≥ 0 ∧ y ≥ 0} {x = a ∧ y = b ∧ x ≥ 0 ∧ y ≥ 0}z := 0{(z + x = a) ∧ (z + y = b) ∧ (x ≥ 0) ∧ (y ≥ 0)}. Aufgabe 3.4 Beweisen Sie mit Hilfe des Hoarekalküls, dass die Sequenz tmp : = a ; a := b ; b : = tmp ; tatsächlich die Inhalte von a und b vertauscht. Fügen Sie entsprechende Zusicherungen in den Programmcode ein. Lösung zu 3.4 Wir bezeichnen die ursprünglichen Inhalte von a bzw. b mit x bzw. y. Dann müssen wir zeigen, dass unter der Vorbedingung {a = x∧ b = y } nach Terminierung des Programmstücks gilt: { a = y∧ b = x} Wir arbeiten von hinten nach vorne: Wir wenden Axiom 2 auf die Anweisung wahre Korrektheitsformel { a = y∧ tmp = x} b := tmp b := tmp und die Nachbedingung { a = y∧ b = x}an und erhalten die { a = y∧ b = x} Jetzt kennen wir die Nachbedingung, die nach der Anweisung 2 an und erhalten { b = y∧ tmp = x} a := b a := b gelten soll. Wir wenden also wieder Axiom { a = y∧ tmp = x} als wahre Korrektheitsformel. Nun noch einmal Axiom 2 anwenden (auf die Anweisung dingung { b = y∧ tmp = x} ) ergibt die wahre Formel tmp := a und die Nachbe- 3. 11 { b = y∧ a = x} tmp := a { b = y∧ tmp = x} Wir können also folgende Zusicherungen in den Programmtext einfügen: { a = x and b tmp : = a {tmp = x and a := b {tmp = x and b : = tmp {b = x and a = y } b = y } a = y} = y } Anwendung der Sequenzregel (zweimal) liefert dann die Behauptung {a = x∧ b = y } Programmcode { a = y∧ b = x} Aufgabe 3.5 Ergänzen Sie das unten abgebildete Flußdiagramm um Zusicherungen, die beweisen, dass hierdurch 2a berechnet wird, falls a ≥ 0 eingegeben wird. Tip: Schleifeninvariante ist {z + 2x = 2a ∧ x ≥ 0}. Start Eingabe x z=0 x>0 nein Ausgabe z ja z = z+2 Ende x = x−1 Abbildung 1: Flussdiagramm zu Aufgabe 3.5 Lösung zu 3.5 Das Flussdiagramm mit Zusicherungen sehen Sie in Abbildung 2. 3. 12 Eingabe x x = a and x >= 0 z=0 x = a and x >= 0 and z = 0 => x>0 nein z + 2x = 2a and x >= 0 Ausgabe z ja z + 2x = 2a and x >= 0 and x !> 0 => z + 2x = 2a and x = 0 => z = 2a z + 2x = 2a and x > 0 => z+2 + 2(x−1) = 2a and x−1 > −1 z = z+2 z + 2(x−1) = 2a and x−1 >= 0 x = x−1 z + 2x = 2a and x >= 0 Abbildung 2: Lösung zu Aufgabe 3.5 4. 13 4 Komplexität Aufgabe 4.1 (KomplBohne) Bestimmen Sie die best-case-, worst-case- und average-case-Komplexität des Bohnenproblems aus der Vorlesung. Welches ist der charakterisierende Parameter? Wann tritt der beste, wann der schlechteste Fall ein? Betrachten Sie Zwei Bohnen ziehen, Bohnen weglegen, Bohne in die Spielurne (zurück-)legen als jeweils einen Arbeitsschritt. Lösung zu 4.1 Characterisierender Parameter für das Urnenproblem ist die Anzahl n der Bohnen, die sich zu Beginn in der Spielurne befinden. In einer Wiederholungsanweiseng werden - zwei Bohnen gezogen - deren Farben miteinander verglichen und - auf jeden Fall eine Bohne wieder zurückgelegt, sodass sich die Anzahl n jedesmal um 1 verringert. Die Schleife wird daher genau (n − 1)-mal ausgeführt. Die Komplexität ist also (unabhängig von der Verteilung der schwarzen und weißen Bohnen) in jedem Fall O(n). Aufgabe 4.2 Bestimmen Sie die genaue Anzahl der Elementaranweisungen, die bei Ablauf des des Programms aus Aufgabe 3.3 ausgeführt werden. Lösung zu 4.2 Es werden vor der Schleife 3 Zuweisungen ausgeführt, in der Schleife wird jeweils einmal der logische Ausdruck ausgewertet, 2 Subtraktionen, 1 Addition und 3 Zuweisungen ausgeführt. Wenn jeder dieser Elementarschritte 1 Kosteneinheit verursacht und m das Minimum von a und b ist, dann sind die Gesamtkosten genau 7m + 3 = 7min(a, b) + 3 Kosteneinheiten. Aufgabe 4.3 a) Folgender Algorithmus bestimmt das Maximum von n Zahlen: Modul Maximum ( n ) i := 1 input (x) max : = x w h i l e ( i < n ) do input ( x) i f ( x > max ) max : = x endif i : = i +1 en d w h ile o u t p u t ( max ) Modulende Bestimmen Sie die best-case- und die worst-case-Komplexität des Verfahrens. Was ist der charakterisierende Parameter? Wann tritt der beste Fall, wann der schlechteste Fall ein? b) Eine kleine Änderung am Algorithmus läßt uns nun nicht das Maximum, sondern die Summe der n eingegebenen Zahlen berechnen: Modul Summe ( n ) i := 1 input (x) sum : = x w h i l e ( i < n ) do input ( x) sum : = sum + x i : = i +1 en d w h ile o u t p u t ( max ) Modulende Wie beeinflußt diese Änderung die best-case- und worst-case-Komplexität des Verfahrens? 4. 14 Lösung zu 4.3 a) Der Aufwand hängt ab von der Anzahl der eingegebenen Zahlen, charakterisierender Parameter ist also n. Wir zählen die Elementaroperationen, die ausgeführt werden (Ein- und Ausgabeoperationen lassen wir unberücksichtigt, sie sind nicht ausschlaggebend für die Qualität des Algorithmus’): • vor der while-Schleife: 2 Wertzuweisungen • Auswertung der Schleifenbedingung i < n: die Schleife wird genau (n − 1)-mal ausgeführt, das erfordert n Vergleiche (Auswertung der Bedingung i < n). • innerhalb der Schleife: n − 1 - mal 1 Vergleich (Auswertung der Bedingung x > max), 1 Addition und 1 oder 2 Wertzuweisungen. Der Gesamtaufwand für die Schleife beträgt also (2n − 1) Vergleiche +(n − 1) Additionen +(n − 1 + k) Wertzuweisungen mit k ∈ {0, 1, . . . n − 1}. Insgesamt ergibt sich im besten Fall (wenn die Anweisung max := x kein einziges mal ausgeführt wird, also k = 0 ist,) 2n − 1 Vergleiche, n − 1 Additionen und n + 1 Wertzuweisungen. Im schlechtesten Fall (die Anweisung max := x wird bei jedem Schleifendurchlauf ausgeführt, also k = n − 1,) kommt man auf 2n − 1 Vergleiche, n − 1 Additionen und 2n Wertzuweisungen. Der beste Fall tritt ein, wenn die größte Zahl als erste eingegeben wird, der schlechteste Fall tritt ein, wenn jede eingegebene Zahl größer ist als das bisherige Maximum, dh. wenn die n Zahlen in aufsteigend sortierter Reihenfolge eintreffen. In jedem Fall (also auch im Durchschnitt) liegt der Aufwand in O(n), wenn man konstante Kosten für die Einzeloperationen ansetzt. b) Wir haben nach wie vor 2 Wertzuweisungen vor der Schleife und n Vergleiche für die Auswertung der Schleifenbedingung. Nur innerhalb der Schleife vereinfacht sich die Analyse: Man hat in jedem Fall 2 Additionen und 2 Wertzuweisungen. Insgesamt ergibt sich ein Aufwand von n Vergleichen, 2(n− 1) Additionen und 2(n− 1)+ 2 Wertzuweisungen. Dieser Aufwand ist bei fester(!) Anzahl n konstant, hängt also nicht von der Größe oder Verteilung oder Sortierung der eingegebenen Werte ab! Die best-case- ,worst-case- und damit auch average-case- Komplexitäten sind gleich (!) und liegen alle in O(n). Achtung! Die Frage ist nicht, für welches n der Aufwand seinen kleinsten oder größten Wert annimmt. Die Aussage die ” best-case Komplexität wird für n = 0 (oder n = 1) erreicht “ ist falsch! Aufgabe 4.4 a) Wie oft wird in dem folgenden Programmstück die Anweisung S ausgeführt? Was ist der charakterisierende Parameter? i := 0 w h i l e ( i < n ) do j := 0 w h i l e ( j < i ) do S; j : = j +1 en d w h ile i := i + 1 en d w h ile b) Welche Laufzeitkomplexität hat das folgende Programmstück? Welche Parameter bestimmen die Laufzeitkomplexität? i f ( n < a ) t h en i := 0 w h i l e ( i < a ) do S; i : = i +1 en d w h ile else i := 0 while ( i < n ) j := 0 while ( j < n ) S; j : = j +1 4. 15 en d w h ile i : = i +1 en d w h ile endif Lösung zu 4.4 a) Die Anweisung S wird in jedem Durchgang der inneren Schleife genau einmal ausgeführt. Diese innere Schleife wird i-mal durchlaufen, wobei i der Zähler für die äußere Schleife ist. Im ersten Durchgang der äußeren Schleife wird S also einmal, im zweiten Durchgang der äußeren Schleife wird S zweimal ausgeführt und so fort. Die äußere Schleife wird aber genau n-mal durchlaufen, nämlich für die Werte i = 0, i = 1, . . . i = n − 1. So wird S insgesamt P( 0 + 1 + 2 + 3 + . . . + (n − 1) = i=0 n − 1) = n(n−1) -mal ausgeführt. 2 Die Komplexität der beiden ineinander geschachtelten Schleifen ist also O(n2 ). b) Die Komplexität wird im wesentlichen von der Anzahl der Schleifendurchläufe bestimmt, wir zählen also wieder, wie oft die Anweisung S erreicht wird. Im Fall n < a wird S genau a-mal ausgeführt, nämlich so oft, wie die erste Schleife durchlaufen wird. Im Fall n ≥ a haben wir wieder zwei ineinander geschachtelte Schleifen, wobei aber nun die innere Schleife genau wie die äußere immer n-mal durchlaufen wird. S wird in diesem Fall also n2 - mal erreicht. Insgesamt ergibt sich für die Anzahl der Ausführungen von S: anz(S) = max(a, n2 ) Die Komplexität des Programms hängt also von den beiden Parametern a und n ab und liegt für alle drei Fälle (worst / average / best case) in O(max(a, n2 )). Aufgabe 4.5 Ein Programm zum Sortieren von n Datensätzen habe eine Laufzeit von t(n) = 2n2 + 7n (gemessen in msec). a) Wie lange benötigt das Programm zum Sortieren von 1000 Datensätzen? b) Wieviele Datensätze können maximal in 1 Stunde sortiert werden? Lösung zu 4.5 a) Die Laufzeit zum Sortieren von n = 1000 Datensätzen ist t(1000) = 2 ∗ 10002 + 7 ∗ 1000[msec] = 2007000[msec] = 2007[sec] = 33.45[min] Das Programm benötigt also ungefähr eine halbe Stunde. b) Wir fragen nun, für welches n die Laufzeit t(n) höchstens 1 Stunde beträgt: t(n) ≤ 1[h] = 60[min] = 3600[sec] = 3600000[msec] ⇔ 2n2 + 7n ≤ 3600000 Wir lösen die quadratische (Un-)Gleichung und erhalten n ≤ 1338. In einer Stunde können also höchstens 1338 Datensätze sortiert werden. Aufgabe 4.6 Beweisen Sie: a) 11n = O(n) n = O(1) b) n−1 c) d) e) f) g) h) 2 4 max(n2 , 10 (n+1) n−2 ) = O(n ) 17n − 5 = O(n) 2n 3n−1 = O(1) 2.5n + 4 = O(n) 2n+5 = O(1) n 1 3 n ) = O(n2 ) min(n2 + 2.4n, 10 4. 16 Lösung zu 4.6 a) Mit n0 = 1 und c = 11 folgt für alle n ≥ n0 : 11n ≤ cn. n ≤ 2 ⇔ n ≤ 2(n − 1) ⇔ n ≤ 2n − 2 ⇔ −n ≤ −2 ⇔ n ≥ 2 gilt mit n0 = 2, c = 2 die b) Wegen n−1 Behauptung. 2 ) = max(n2 , 10(n + 1)2 n2 ) = 10(n4 + 2n3 + n2 ) ≤ 10(n4 + 2n4 + n4 ) = 40n4 für c) Es ist max(n2 , 10(n+1) n−2 alle n ∈ N. Also kann man n0 = 1 und c = 40 wählen und es folgt die Behauptung. d) Wir müssen zeigen, dass es ein c ≥ 0 und ein n0 ≥ 0 gibt, sodass für alle n ≥ n0 gilt: cf (n) ≥ g(n) mit f (n) = n und g(n) = 17n − 5. Wir wählen c = 17 und n0 = 0. 2n e) Hier müssen wir ein c und ein n0 finden, sodass für alle n ≥ n0 gilt: 3n−1 ≤ c. Dies erreicht man mit c = 1 und n0 = 1. f) Gesucht sind ein c ≥ 0 und ein n0 ≥ 0, sodass für alle n ≥ n0 gilt: g(n) ≤ cf (n) für f (n) = n und g(n) = 2.5n + 4. Eine erste grobe Abschätzung liefert 2.5n + 4 < 2.5n + 4n < 7n für alle n ≥ 1. c = 7 und n0 = 1 erfüllen also die Bedingung. Man kann aber auch etwas knapper abschätzen: Wegen 2.5n + 4 ≤ 3n ⇔ 4 ≤ 0.5n ⇔ n ≥ 8 könnte man auch c = 3 und n0 = 8 wählen. Es gibt weitere (genauer: unendlich viele) Zahlenpaare c, n0 , die die Ungleichung ∀n ≥ n0 : 2.5n + 4 ≤ cn erfüllen. ≤ c ∗ 1 erfüllt. Auch hier gibt es g) Wir suchen nun ein Zahlenpaar c, n0 , das die Bedingung ∀n ≥ n0 : 2n+5 n wieder verschiedene Möglichkeiten zur Abschätzung: 2n+5 = 2 + n5 ≤ 2 + 5 = 7 für n ≥ 1, man kann also c = 7, n0 = 1 wählen, oder n 2n+5 = 2 + n5 ≤ 3 ⇔ n5 ≤ 1 ⇔ 5 ≤ n. Die Bedingung 2n+5 ≤ c für alle n ≥ n0 ist also auch für c = 3 und n n n0 = 5 erfüllt. 1 3 h) Wir untersuchen zunächst, welcher der beiden Ausdrücke n2 + 2.4n und 10 n das Minimum bildet. Dazu 1 3 2 müssen wir überlegen, in welchen Bereichen n + 2.4n kleiner bzw. größer ist als 10 n . 1 1 3 2 2 Dazu bestimmen wir die Nullstellen von 10 n − (n + 2.4n) = 10 n(n − 10n − 24) und erhalten n1 = 0, n2 = 12, n3 = −2. (Zur Erinnerung: Die Nullstellen einer quadratischen Gleichung x2 + px + q berechnen sich nach der Formel x1,2 = − 2p ± q p2 4 − q .) 1 3 1 3 1 3 Für n > 12 ist 10 n − n2 − 2.4n positiv, also 10 n > n2 + 2.4n, also min(n2 + 2.4n, 10 n ) = n2 + 2.4n. 2 2 Für n > 12 ist aber auch n + 2.4n ≤ 2n , also können wir c = 2 und n0 = 12 wählen und erhalten 1 3 n ) = O(n2 ). min(n2 + 2.4n, 10 Aufgabe 4.7 Bestimmen Sie die best-case- und die worst-case-Komplexität des Programms z := 0 u := x w h i l e u > 0 do z := z + y u := u − 1 en d w h ile Was ist der charakterisierende Parameter? Lösung zu 4.7 Die Laufzeit des Programms hängt vom Wert der Eingabe x ab, x ist also der charakterisierende Parameter. Die Schleife wird genau x-mal durchlaufen, in jedem Schleifendurchlauf werden 4 Elementaroperationen ausgeführt (1 Addition, 1 Subtraktion, 2 Wertzuweisungen), also entsteht insgesamt ein Aufwand von 4x Einheiten für den Schleifenrumpf und x + 1 Einheiten für die Auswertung der Schleifenbedingung. Mit den beiden Zuweisungen vor der Schleife hat das Programm eine Laufzeit von 3 + 5x = O(x). Diese Laufzeit ist bei festem x immer gleich, also sind best-case-, worst-case- und average-case-Komplexität alle gleich O(x). 5. 17 5 Abstrakte Datentypen Aufgabe 5.1 Erweitern Sie den abstrakten Datentyp folge um a) b) c) d) die Operation length, die die Anzahl der Elemente (Länge) der Folge bestimmt die Operation isin, die überprüft, ob eine Folge ein gegebenes Element enthält die Operation position, die die Positionsnummer eines Elementes in der Folge liefert die Operation insert, die eine neue Folge bestimmt, indem ein neues Element elem an einer bestimmten Stelle pos in die alte Folge eingefügt wird e) die Operation delete, die eine neue Folge bestimmt, indem das Element an der Stelle pos aus der Eingangsfolge entfernt wird f) die Operation get, die das Element an der pos-ten Position liefert Lösung zu 5.1 a) length : M ∗ → N length(f ) = 0 falls isemptyf olge(f ) 1 + length(rest(f )) sonst b) isin : M ∗ × M → B falls isemptyf olge(f ) falsch wahr falls a = f irst(f ) isin(f, a) = isin(rest(f ), a) sonst c) position : M ∗ × M → N0 falls ¬isin(f, a) 0 position(f, a) = 1 falls a = f irst(f ) 1 + position(rest(f ), a) sonst d) insert : M ∗ × M × N0 → M ∗ ⊥ falls pos > length(f ) + 1 oder pos = 0 concat(makef olge(elem), f ) falls pos = 1 insert(f, elem, pos) = concat(makef olge(f irst(f )), insert(rest(f ), elem, pos − 1)) sonst e) delete : M ∗ × N → M ∗ ⊥ falls pos > length(f ) oder pos = 0 rest(f ) falls pos = 1 delete(f, pos) = concat(makef olge(f irst(f )), delete(rest(f ), pos − 1) sonst f) get : M ∗ × N0 → M falls pos > length(f ) oder pos = 0 ⊥ f irst(f ) falls pos = 1 get(f, pos) = get(rest(f ), pos − 1) sonst Aufgabe 5.2 Betrachten wir den Datentyp folge auf der Menge M der natürlichen Zahlen (einschließlich 0): M = N. a) Definieren Sie eine Operation sum, die die Summe der Folgenelemente berechnet. Beispiel: sum(< 1, 3, 2, 4 >) = 1 + 3 + 2 + 4 = 10 b) Definieren Sie eine Operation add, die aus zwei (gleichlangen!) Folgen eine neue Folge bestimmt, indem die Folgenglieder komponentenweise addiert werden. Beispiel: add(< 1, 3, 5, 7 >, < 4, 2, 6, 0 >) =< 5, 5, 11, 7 > 5. 18 Lösung zu 5.2 a) sum : N∗ → N sum(f ) = 0 falls isemptyf olge(f ) f irst(f ) + sum(rest(f )) sonst b) add : N∗ × N∗ → N add(f1 , f2 ) = ⊥ falls length(f1 ) 6= length(f2) emptyf olge() falls length(f1 ) = length(f2) = 0 concat( makef olge(f irst(f1) + f irst(f2 )), add(rest(f1 ), rest(f2 )) ) sonst Aufgabe 5.3 Betrachten wir den Datentyp folge auf der Menge M der natürlichen Zahlen (einschließlich 0): M = N. a) Definieren Sie eine Operation max, die das Größte der Folgenelemente bestimmt. Beispiel: max(< 1, 3, 2, 4 >) = 4 b) Definieren Sie eine Operation vmax, die aus zwei (gleichlangen!) Folgen eine neue Folge bestimmt, indem die Folgenglieder komponentenweise miteinander verglichen werden und das jeweils größere in die neue Folge übernommen wird. Beispiel: vmax(< 1, 3, 5, 7 >, < 4, 2, 6, 0 >) =< 4, 3, 6, 7 > Lösung zu 5.3 a) max : N∗ → N ⊥ f irst(f ) max(f ) = maximum(f irst(f ), max(rest(f ))) b) vmax : N∗ × N∗ → N∗ vmax(f1 , f2 ) = falls isemptyf olge(f ) falls length(f ) = 1 sonst ⊥ emptyf olge() makef olge(maximum(f irst(f1), f irst(f2 ))), concat( makef olge(maximum(f irst(f1), f irst(f2 ))), vmax(rest(f1 ), rest(f2 ))) falls length(f1 ) 6= length(f2) falls length(f1 ) = length(f2) = 0 falls length(f1 ) = length(f2) = 1 sonst Dabei ist maximum : N × N → N die auf den natürlichen Zahlen bekannte Funktion maximim(x, y) = x y falls x ≥ y sonst Aufgabe 5.4 Definieren Sie eine Operation minimum(f), die das kleinste Element einer Folge f natürlicher Zahlen bestimmt. Schreiben Sie ein Modul (wie die Module spiegeln, auswerten, ersetzen aus der Vorlesung) zur Implementierung dieser Operation. Lösung zu 5.4 minimum : N∗ → N falls isemptyf olge(f ) ⊥ f irst(f ) falls f irst(f ) ≤ minimum(rest(f )) minimum(f ) = minimum(rest(f )) sonst 5. 19 Modul minimum(f ) wenn nicht isemptyfolge(folge) dann minimum := first(f) sonst minimum := ⊥ end wenn solange nicht isemptyfolge(f) wenn first(f) < minimum dann minimum := first(f) end wenn f := rest(f) end solange Modulende Aufgabe 5.5 Im Skript zur Vorlesung finden Sie folgende Definition 1 Sei M eine total geordnete Menge. Ein Baum t ∈ M △ heißt (binärer) Suchbaum über M , falls für jeden Knoten k in t gilt: Alle Knoten im linken Teilbaum von k sind kleiner als die Wurzel von k und alle Knoten im rechten Teilbaum von k sind größer als die Wurzel. △ Wir bezeichnen die Menge alle Suchbäume über M mit M< . In einem binären Suchbaum sind die Knoteneinträge also paarweise verschieden. Außerdem gilt: die inorder-Folge eines binären Suchbaums liefert die Einträge aufsteigend sortiert. a) Prüfen Sie, ob die beiden Bäume aus Abb. 3 binäre Suchbäume (über der Menge der natürlichen Zahlen M = N mit der üblichen kleiner-Relation) sind. b) Beweisen oder widerlegen Sie die Aussage: Wenn l und r zwei binäre Suchbäume sind und root(l) < a < root(r), dann ist auch < l, a, r > ein Suchbaum. c) Definieren Sie eine Operation istSuchbaum(t), die einen Baum t darauf prüft, ob er ein binärer Suchbaum ist. d) Definieren Sie eine Operation insert(t, a), die einen neuen Wert a in einen Suchbaum t einfügt (so dass die Suchbaumeigenschaft erhalten bleibt). e) Implementieren Sie die Operation insert in einem Modul. f) Bestimmen Sie die best-case- und die worst-case-Komplexität Ihrer Implementierung. Was ist der charakterisierende Parameter? Wann tritt der beste, wann der schlechteste Fall ein? 45 5 7 3 1 7 4 (a) 6 80 12 2 53 27 (b) Abbildung 3: Bäume zu Aufgabe 5.5 Lösung zu 5.5 a) Bei dem Baum aus Abbildung 3(a) verletzt der Eintrag 2 die Suchbaumeigenschaft, der Baum aus (b) ist dagegen ein Suchbaum. b) Die Aussage ist falsch, denn es läßt sich leicht ein Gegenbeispiel konstruieren: l =< 3, 5, 7 > und r =< 1, 13, 14 > sind zwei Suchbäume und ihre Wurzeleinträge sind kleiner bzw. größer als (z.B.) 11, aber < l, 11, r > ist kein Suchbaum. c) Wie wir gesehen haben, genügt es nicht, die Wurzeln der beiden Teilbäume mit der Wurzel des Gesamtbaums zu vergleichen, sondern man muß den größten Wert des linken Teilbaums und den kleinsten Wert des rechten 5. 20 Teilbaums mit dem Wurzelwert des Gesamtbaums vergleichen. Dazu definieren wir uns zwei Hilfsoperationen tmin : M △ → N und tmax : M △ → N: tmin : M △ → N ⊥ falls isemptytree(t) root(t) falls lef ttree(t) = righttree(t) = ε min(tmin(lef ttree(t)), root(t)) falls lef ttree(t) 6= ε ∧ righttree(t) = ε tmin(t) = min(tmin(righttree(t)), root(t)) falls righttree(t) 6= ε ∧ lef ttree(t) = ε min( tmin(lef ttree(t)), root(t), tmin(righttree(t)) ) sonst und tmax : N△ → N durch ⊥ root(t) max(tmax(lef ttree(t)), root(t)) tmax(t) = max(tmax(righttree(t)), root(t)) max( tmax(lef ttree(t)), root(t), tmax(righttree(t)) falls isemptytree(t) falls lef ttree(t) = righttree(t) = ε falls lef ttree(t) 6= ε ∧ righttree(t) = ε falls righttree(t) 6= ε ∧ lef ttree(t) = ε ) sonst Hierbei bezeichnen min bzw. max die bekannten Funktionen, die die kleinste bzw. größte von mehreren natürlichen Zahlen bestimmen. Dann können wir istSuchbaum : M △ → B so definieren: ⊥ falls t = ε wahr falls lef ttree(t) = ε ∧ righttree(t) = ε wahr falls righttree(t) = ε ∧ lef ttree(t) 6= ε ∧ istSuchbaum(lef ttree(t)) ∧ (max(lef ttree(t) < root(t)) wahr falls lef ttree(t) = ε ∧ righttree(t) 6= ε istSuchbaum(t) = ∧ istSuchbaum(righttree(t)) ∧ (min(righttree(t) > root(t)) wahr falls lef ttree(t) 6= ε ∧ righttree(t) 6= ε ∧ istSuchbaum(righttree(t)) ∧ istSuchbaum(lef ttree(t)) ∧ (max(lef ttree(t) < root(t) < min(righttree(t)) f alsch sonst d) Idee: Wenn der Baum t leer ist, erzeugen wir einen Baum mit dem (einzigen) Eintrag a. Wenn root(t) = a ist, dann lassen wir den Baum t unverändert, (denn dann ist kein Einfügen mehr notwendig, der Wert a ist ja schon im Baum enthalten). Ansonsten entscheiden wir wie bei contains, ob a im linken oder im rechten Teilbaum von t eingefügt werden muß. insert : M △ × M → M △ maketree(ε, a, ε) falls t = ε maketree(insert(lef ttree(t), a), root(t), righttree(t)) falls a < root(t) insert(t, a) = maketree(lef ttree(t), root(t), insert(righttree(t), a)) falls a > root(t) t falls a = root(t) e) Modul insert(t, a) wenn ¬ isemptytree(t) dann wenn a < root(t) dann t := maketree(insert(lefttree(t), a), root(t), righttree(t)) sonst wenn a > root(t) dann t := maketree(lefttree(t), root(t), insert(righttree(t), a)) end wenn end wenn sonst t := maketree(ε, a, ε) end wenn insert := t Modulende f) Einfügen eines neuen Wertes geschieht immer als Blatt, nie als innerer Knoten. Es muß also bei jedem Einfügen ein Pfad im Baum von der Wurzel bis zum Blatt verfolgt werden. Die Kosten sind dann proportional zur Länge des Pfades. 5. 21 Der Aufwand für eine Einfüge-Operation hängt also nicht nur von der Anzahl n der Knoten, sondern auch von der Gestalt des Baumes und damit von der Reihenfolge ab, in der die n Werte eingefügt wurden. Es genügt also nicht, eine einzelne insert-Operation zu betrachten, sondern wir müssen alle vorangegangenen Einfügungen berücksichtigen. Anders ausgedrückt: Wenn wir nach günstigen und ungünstigen Fällen suchen, müssen wir nach guten bzw. schlechten Einfüge-folgen suchen. Der beste Fall tritt ein, wenn die Pfade im Baum möglichst kurz sind. Gute Folgen sind also so gemischt, dass der entstehende Baum möglichst ausbalanciert“ ist, die Pfadlänge ist dann für alle Pfade in O(log n). ” Der Gesamtaufwand für alle Einfügeoperationen liegt in O(log 1) + O(log 2) + . . . + O(log n) = O(n log n). Der zu erwartende Aufwand für eine Einfügeoperation ist dann in O(log n). Ungünstig ist, wenn der Baum einen extrem langen und ansonsten nur sehr kurze Pfade enthält, im Extremfall entartet er zur Liste. Dies ist der schlechteste Fall. Er tritt ein, wenn die Einfügefolge bereits sortiert ist. Die Kosten für alle Einfügeoperationen sind dann nämlich in O(1) + O(2) + . . . + O(n) = O(n2 ) und die Kosten pro einzelner Einfügeoperation liegen demnach in O(n). Aufgabe 5.6 Entwickeln Sie einen Algorithmus (in Form eines Moduls oder eines Block-Strukturdiagramms), der aus einer Folge, die natürliche Zahlen in beliebiger Reihenfolge enthält, eine sortierte Folge erzeugt. Tip: Dazu gibt es viele Möglichkeiten, z.B. • Verwenden Sie zwei stacks als Hilfsmittel, von denen der erste nur Zahlen enthält, die kleiner sind als alle Werte im zweiten. Außerdem sollen die Zahlen im ersten stack von unten nach oben aufsteigend sortiert sein, die im zweiten umgekehrt oder... • definieren Sie zum Datentyp f olge eine Operation delete(f, a), die ein Element aus einer Folge entfernt und benutzen Sie diese zusammen mit minimum aus Aufgabe 5.4 oder... • verwenden Sie die Ergebnisse aus Aufgabe 5.5. Lösung zu 5.6 • Sortieren durch Einfügen Die beiden stacks zusammengenommen (den ersten von unten nach oben gelesen, den zweiten von oben nach unten) enthalten zu jedem Zeitpunkt eine sortierte Teilfolge von f . Es wird sukzessive das nächste Element der Folge f entnommen und durch umschaufeln“ der beiden stacks die richtige Stelle zum Einfügen ” gesucht. Modul SortierenDurchEinfuegen(f) s1 := emptystack() s2 := emptystack() while not isemptyf olge(f ) do a := f irst(f ) f := rest(f ) while not isemptystack(s1) and top(s1) > a do push(s2, top(s1)) pop(s1) endwhile while not isemptystack(s2) and top(s2) < a do push(s1, top(s2)) pop(s2) endwhile push(a1, a) endwhile while not isemptystack(s1) do push(s2, top(s1)) pop(s1) endwhile while not isemptystack(s2) do f := concat(f, makef olge(top(s2))) 5. 22 pop(s2) endwhile return f modulende • Sortieren durch Auswaehlen Man entnimmt der Folge f das kleinste Element und fügt es an eine neue Folge hinten an. Dazu benötigt man neben der Minimumsuche eine Operation zum Entfernen eines Elementes aus einer Folge, etwa: delete : M ∗ × M → M ∗ falls ¬contains(f, a) f delete(f, a) = rest(f ) falls a = f irst(f ) concat(makef olge(f irst(f )), delete(rest(f ), a)) sonst Damit kann man dann programmieren: Modul SortierenDurchAuswaehlen(f) g := emptyf olge() while not isemptyf olge(f ) do a := minimum(f ) f := delete(f, a) g := concat(g, makef olge(a)) endwhile return g Modulende • Baumsortieren In Aufgabe 5.5 war schon bemerkt worden, dass die inorder-Folge eines binären Suchbaums die Knoten in sortierter Reihenfolge liefert. (Man kann auch beweisen, dass dies immer so ist.) Diese Beobachtung führt dann zu folgender Lösung: Modul BaumSortieren(f) t := emptytree() while not isemptyf olge(f ) do t := insert(t, f irst(f )) f := rest(f ) endwhile BaumSortieren := inorder(t) Modulende 6. 23 6 Formale Sprachen, BNF, EBNF Aufgabe 6.1 Betrachten Sie die Grammatik G = < {a, b}, {S, A, B}, P, S > mit den Produktionen P (in BNF): S ::= AbB A ::= ε | aaA B ::= ε | bbB | S Gehören aaaabbb, baaabbb und bbbaab zu L(G)? Geben Sie ggf. eine Ableitung an. Lösung zu 6.1 S ⊢ AbB ⊢ aaAbB ⊢ aaaaAbB ⊢ aaaabB ⊢ aaaabbbB ⊢ aaaabbb ist eine Ableitung. baaabbb ist sicherlich nicht in L(G), denn man überlegt sich schnell (exakt: mit vollständiger Induktion über die Länge der Ableitung), dass alle Wörter, die aus S abgeleitet werden können, eine gerade Anzahl von a’s enthalten. S ⊢ AbB ⊢ bB ⊢ bS ⊢ bAbB ⊢ bbB ⊢ bbS ⊢ bbAbB ⊢ bbbB ⊢ bbbS ⊢ bbbAbB ⊢ bbbaaAbB ⊢ bbbaabB ⊢ bbbaab ist eine Ableitung. Aufgabe 6.2 a) Geben Sie eine Grammatik an, die die Sprache L = {an bm cn | n, m ∈ N0 } erzeugt. b) Von welchem Typ ist Ihre Grammatik? c) Geben Sie jeweils eine Ableitung für die Wörter ac, b, und aabbbcc an. Lösung zu 6.2 a) G =< {a, b, c}, {S, T }, P, S} mit P = {S → aSc, S → bT, S → b, S → ǫ, T → bT, T → b} erzeugt die gewünschte Sprache. b) G ist kontextfrei. c) S ⊢ aSc ⊢ ac S⊢b S ⊢ aSc ⊢ aaScc ⊢ aabT cc ⊢ aabbT cc ⊢ aabbbcc Aufgabe 6.3 In vielen Programmiersprachen dürfen Bezeichner (d.s. die vom Programmierer wählbaren Namen für Programme, Prozeduren, Variablen usf.) aus einer beliebigen Folge von Buchstaben und Ziffern bestehen, aber sie müssen mit einem Buchstaben beginnen. a) b) c) d) e) Geben Sie eine Grammatik für Bezeichner an. Leiten Sie den Bezeichner Name1a ab. Geben Sie eine BNF für Bezeichner an. Geben Sie eine EBNF für Bezeichner an. Geben Sie ein Syntaxdiagramm für Bezeichner an. 6. 24 Lösung zu 6.3 a) G =< {a, b, . . . z, A, B, . . . Z, 0, 1 . . . 9}, {Start, Buchstabe, Zeichenfolge , Ziffer }, P, Start > mit P = { Start → Buchstabe Zeichenfolge Buchstabe →a Buchstabe →b .. . →Z → Buchstabe Zeichenfolge → Ziffer Zeichenfolge →ε →0 →1 Buchstabe Zeichenfolge Zeichenfolge Zeichenfolge Ziffer Ziffer .. . b) Start Ziffer →9 } ⊢ Buchstabe Zeichenfolge ⊢ N Zeichenfolge ⊢ N Buchstabe Zeichenfolge ⊢ N a Zeichenfolge ⊢ N a Buchstabe Zeichenfolge ⊢ N a m Zeichenfolge ⊢ N a m Buchstabe Zeichenfolge ⊢ N a m e Zeichenfolge ⊢ N a m e Ziffer Zeichenfolge ⊢ N a m e 1 Zeichenfolge ⊢ N a m e 1 Buchstabe Zeichenfolge ⊢ N a m e 1 a Zeichenfolge ⊢ N a m e 1 a ε = Name1a c) BNF: < Bezeichner > < Buchstabe > ::= ::= < Buchstabe > | < Buchstabe >< Zeichenfolge > a | b | ... | z | A | B | ... | Z < Zeichenfolge > ::= < Buchstabe > | < Ziffer > | | < Ziffer > ::= < Buchstabe >< Zeichenfolge > < Ziffer >< Zeichenfolge > 0 | 1 | ... | 9 d) EBNF: < Bezeichner > ::= < Buchstabe > {< Buchstabe > | < Ziffer >} < Buchstabe > ::= a | b | . . . | z | A | B | . . . | Z < Ziffer > ::= 0 | 1 | . . . | 9 e) Syntaxdiagramm: siehe Abb. 4 Buchstabe Bezeichner Buchstabe Ziffer Abbildung 4: Syntaxdiagramm für Bezeichner 6. 25 Aufgabe 6.4 Erweitern Sie das Syntaxdiagramm für Ausdrücke aus der Vorlesung um einzelne Bezeichner und Methodenaufrufe. Geben Sie ein entsprechendes Syntaxdiagramm für logische Ausdrücke an. Lösung zu 6.4 Teildiagramm für < Bezeichner > wie in Aufgabe 6.3. Teildiagramm für < Zahl > wie in Folien. Ausdruck Zahlkonst ( Ausdruck ) Ausdruck + Ausdruck Ausdruck − Ausdruck Ausdruck * Ausdruck Ausdruck / Ausdruck Methodenaufruf Bezeichner Methodenaufruf Bezeichner ( Ausdruck ) , Zahlkonst Zahl Gleitzahl Bezeichner true false logAusdruck ( logAusdruck ) ( logAusdruck logAusdruck ) ( logAusdruck logAusdruck ) = Ausdruck Ausdruck < < ... Methodenaufruf 6. 26 Aufgabe 6.5 Bei der Darstellung von Modulen haben wir bisher intuitiv gewisse Regeln eingehalten, z.B. haben wir Verzweigungen mit endif und Schleifen mit endwhile abgeschlossen. Fassen Sie die Regeln unserer Modulsprache“ in ” Syntaxdiagrammen zusammen. Lösung zu 6.5 Modul Modul Bezeichner Anweisung Modulende Anweisung Wertzuweisung Verzweigung Schleife Wertzuweisung logAusdruck Bezeichner := Ausdruck Verzweigung if logAusdruck then Anweisung else Anweisung Schleife while logAusdruck do Anweisung endwhile endif 6. 27 Aufgabe 6.6 In Java gelten folgende Syntaxregeln für die Darstellung von Gleitkommazahlen: Gleitkommazahlen bestehen aus einem optionalen Vorzeichen (+ oder -), einer Ziffernfolge (ohne führende Nullen oder = 0) für den Vorkommaanteil und einem Nachkommaanteil oder einer Exponentangabe (oder beidem). Der Nachkommaanteil besteht aus einem Punkt und einer nichtleeren Ziffernfolge, die Exponentangabe besteht aus einem e oder E, gefolgt von einem optionalen Vorzeichen und mindestens einer, maximal zwei Ziffern. Beispiel: +3.14 3. 2.0e-7 0 3498E3 2.5e sind gültige Darstellungen, sind keine gültigen Darstellungen. a) Geben Sie ein Syntaxdiagramm für die Darstellung von Gleitkommazahlen an. b) Geben Sie eine EBNF für die Darstellung von Gleitkommazahlen an. Lösung zu 6.6 a) In Abbildung 5 ist das Syntaxdiagramm für eine Gleitkommazahl angegeben. + Gleitzahl Ziffer − . Ziffer0 e + E − Ziffer Ziffer Ziffer Ziffer Abbildung 5: Syntaxdiagramm zu Aufgabe 6.6 b) < Gleitzahl < Vorzeichen < Vorkommateil < Gleitkommateil < Nachkommateil < Expoteil < exp > > > > > > > ::= ::= ::= ::= ::= :== ::= [< Vorzeichen >] < Vorkommateil >< Gleitkommateil > + | < GanzZahl > < Nachkommateil > [< Expoteil >] | [< Nachkommateil >] < Expoteil > . < Ziffer > {< Ziffer >} < exp > [< Vorzeichen >] < Ziffer > {< Ziffer >} e | E Aufgabe 6.7 Geben Sie eine EBNF für Telefonbucheinträge an. Ein Eintrag besteht aus einem (Nach-)Namen, beliebig vielen Vornamen, einer mindestens 3-, maximal 5-stelligen Vorwahl, die mit 0 beginnt, einem Schrägstrich und einer mindestens 3-stelligen Rufnummer, die aber nicht mit 0 beginnen darf. Optional dürfen zu einem Namen maximal zwei weitere, durch Kommas abgetrennte Telefonnummern angegeben werden. Lösung zu 6.7 < Eintrag < Name < Vorname < Buchstabe < Telnr < Vorwahl < Rufnr < Zahl < ZifferOhne0 < Ziffer > > > > > > > > > > ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= < Name > [< Vorname >] < T elnr > [, < T elnr >] [, < T elnr >] < Buchstabe >< Name > | < Buchstabe > < Name > [< Name >] A | B | . . . |z < Vorwahl > / < Rufnr > 0 < Ziffer >< Ziffer > [< Ziffer >][< Ziffer >] < ZifferOhne0 >< Ziffer >< Zahl > < Ziffer > {< Ziffer >} 1 | 2 | . . . |9 0 | < ZifferOhne0 > 6. 28 Aufgabe 6.8 Geben Sie eine EBNF für die Signaturen des Buchbestandes der Bibliothek an. Eine solche Signatur besteht aus 1-3 Großbuchstaben, einer maximal 4-stelligen Ziffernfolge ohne führende Nullen, einer optionalen Angabe über die Auflage und einer ebenfalls optionalen Angabe über die Anzahl der Exemplare. Die Auflage wird als Ziffernfolge (ohne führende Nullen) in runden Klammern angegeben, die Exemplaranzahl durch ein +-Zeichen und eine Ziffernfolge (ohne führende Nullen). Beispiel: TVI 1067 +7 TYD 27 (2) TVA 16 (2) +10 und TZU 87 sind korrekte Signaturen. Lösung zu 6.8 < Signatur > < Auflage > < Anz > < Buchstabe > < Ziffer > < ZifferOhne0 > ::= ::= ::= ::= ::= ::= < Buchstabe > [< Buchstabe >] [< Buchstabe >] < ZifferOhne0 > [< Ziffer >] [< Ziffer >] [< Ziffer >] [< Auflage >] [< Anz >] ( < ZifferOhne0 > {< Ziffer >} ) + < ZifferOhne0 > {< Ziffer >} a|b| . . . |z|A| . . . Z 0 | < ZifferOhne0 > 1| . . . |9 7. 29 7 Zahldarstellung Aufgabe 7.1 Vervollständigen Sie folgende Tabelle: dezimal dual oktal hexadezimal Zweierkomplement 18 0101 1101 207 A6 Lösung zu 7.1 dezimal 18 93 135 166 dual 0001 0101 1000 1010 0010 1101 0111 0110 oktal 22 135 207 246 hexadezimal 12 5D 87 A6 Zweierkomplement 1110 1110 1010 0011 0111 1001 0101 1010 hexadezimal Zweierkomplement Aufgabe 7.2 Vervollständigen Sie folgende Tabelle: dezimal dual oktal 23 0111 1001 315 4C Lösung zu 7.2 dezimal 23 121 205 76 dual 0001 0111 1100 0100 0111 1001 1101 1100 oktal 27 171 315 114 hexadezimal 17 79 CD 4C Zweierkomplement 1110 1001 1000 0111 0011 0011 1011 0100 Aufgabe 7.3 Stellen Sie die Zahlen x = 2304, y = -4096 und z = 4096.001953125 in IEEE-Darstellung mit 32 bit dar. Lösung zu 7.3 Die Dualdarstellung von 2304 ist 1001 0000 00002 = 1,0012 ∗ 211 . Die Mantisse ist also 001 0000 0000 0000 0000 0000, der Exponent ist 11+127 = 138 = 1000 10102 und das Vorzeichen ist 0. Insgesamt: 0100 0101 0001 0000 0000 0000 0000 0000 Die Dualdarstellung von 4096.001953125 ist 1 0000 0000 0000, 0000 0000 12 = 1, 0000 0000 0000 0000 0000 12 ∗ 212 . Mantisse = 0000 0000 0000 0000 0000 1000, Exponent = 12 + 127 = 139 = 1000 10112 und Vorzeichen = 0. Insgesamt: 0100 0101 1000 0000 0000 0000 0000 0100 7. 30 Aufgabe 7.4 Warum hat der Dezimalbruch 0.1 im dualen keine endliche Darstellung? (siehe Skript: 0.110 = 0.000112 ) Lösung zu 7.4 Wir nehmen an, es gebe eine endliche Darstellung dh. es gebe eine natürliche Zahl n und Pn der 0.1 im Dualsystem, Pn 1 Koeffizienten k1 , . . . kn ∈ {0, 1}, sodass 10 = i=1 ki 2−i = 21n i=1 ki 2n−i . Pn n Dann müßte i=1 ki 2n−i = 210 sein. Dies ist aber unmöglich, weil die rechte Seite niemals eine ganze Zahl sein kann (10 enthält den Primfaktor 5, der sich in keiner Zweierpotenz herauskürzen läßt). 1 Aufgabe 7.5 Stellen Sie die Dezimalzahlen 12.625 und -24.3125 im 32-bit-IEEE-Format dar. Lösung zu 7.5 12.625 = 23 + 22 + 2−1 + 2−3 = 1100,1012 = 1,1001012 ∗ 23 Das Vorzeichen ist 0, die Mantisse ist 100 1010 0000 0000 0000 0000 und der Exponent ist 3 + 127 = 13010 = 1000 00102 . Also wird 12.625 dargestellt als 0100 0001 0100 1010 0000 0000 0000 0000. −24.3125 = 24 + 23 + 2−2 + 2−4 = -11000,01012 = -1,100001012 ∗ 24 Hier ist das Vorzeichen 1, die Mantisse ist 100 0010 1000 0000 0000 0000 und der Exponent ist 4 + 127 = 13110 = 1000 00112 . Insgesamt: −24.3125 ≡ 1100 0001 1100 0010 1000 0000 0000 0000. Aufgabe 7.6 Übertragen Sie 0100 1001 0111 0100 0010 0100 0000 0000 aus der 32-bit IEEE-Darstellung in Dezimalschreibweise. Lösung zu 7.6 Wir zerlegen die Zahl in ihre Bestandteile: V = 0, E = 1001 00102 = 14610, M = 111 0100 0010 0100 0000 0000. Der Wert beträgt also (−1)V ∗ (1.M ) ∗ (2E−127 ) = 1.111 0100 0010 0100 0000 0000 ∗ 219 = 1111 0100 0010 0100 00002 = 1.000.00010 1 Dieses Argument läßt sich verallgemeinern: Die rationalen Zahlen, die sich in einem b-adischen System (ein Zahlsystem zur Basis b) mit endlich vielen Nachkommastelen darstellen lassen, sind genau diejenigen, deren Nenner (nach weitestmöglichem Kürzen) keine anderen Primfaktoren enthält als b.