DHP-FAQ Datenbanken Compilerbau Software Engineering Objektorientierung Rechnerarchitektur Betriebssysteme Rechnernetze Rechnernetze II Effiziente Algorithmen und Datenstrukturen Automaten, Formale Sprachen, Berechenbarkeit Logik http://www.deissenboeck.de/faqs DHP-FAQ Wie auch bei den DVP-FAQs sind hier wieder alle, mir relevant erscheinenden, Fragen aus den Protokollen zusammengefasst. Wieder gilt, dass die Antworten zum grossen Teil direkt aus der angegebenen Literatur übernommen wurden und daher nicht auf meine geistige Leistung zurück gehen. Trotzdem kann es natürlich gut sein, dass ein Haufen Fehler drin sind. Viel Spass damit. Florian Deißenböck, 03.04.2003 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 2 Inhaltsverzeichnis I Praktische Informatik 5 1 Datenbanksysteme 1.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . 1.2 Datenbankentwurf . . . . . . . . . . . . . . . . . . . 1.3 Das relationale Modell . . . . . . . . . . . . . . . . . 1.4 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5 Datenintegrität . . . . . . . . . . . . . . . . . . . . . 1.6 Entwurfstheorie (Normalformen) . . . . . . . . . . . 1.7 Transaktionen . . . . . . . . . . . . . . . . . . . . . . 1.8 Objektrelationale und objektorientierte Datenbanken 1.9 Data Warehousing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 8 10 12 13 13 15 16 17 2 Compilerbau 2.1 Allgemeines . . . . . . . . . . . . . . . . 2.2 Lexikalische Analyse . . . . . . . . . . . 2.3 Syntaktische Analyse . . . . . . . . . . . 2.4 Semantische Analyse . . . . . . . . . . . 2.5 Codegenerierung und Optimierung . . . 2.6 Übersetzung objektorientierter Sprachen 2.7 Top-Down Parser in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 19 20 21 23 23 25 26 . . . . . . . . . . . . . . Elicitation) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 30 31 31 32 33 33 35 36 . . . . . . . . . . . . . . . . . . . . . 3 Software Engineering 3.1 Allgemeines . . . . . . . . . . . . . . . . . . . 3.2 UML . . . . . . . . . . . . . . . . . . . . . . . 3.3 Ermittlung der Anforderungen (Requirements 3.4 Analyse . . . . . . . . . . . . . . . . . . . . . 3.5 Grobentwurf (System Design) . . . . . . . . . 3.6 Feinentwurf (Object Design) . . . . . . . . . . 3.7 Testen . . . . . . . . . . . . . . . . . . . . . . 3.8 Projektmanagement . . . . . . . . . . . . . . 3.9 Vorgehensmodelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Objektorientierung 41 4.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4.2 Entwurfsmuster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Literaturverzeichnis 43 II 45 Technische Informatik 5 Rechnerarchitektur 5.1 Allgemeines . . . . . . . . . . . . 5.2 Zentralprozessoren . . . . . . . . 5.3 Hauptspeicher und Verkehrswege 5.4 Ein-/Ausgabewerk . . . . . . . . . . . . . . . . DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 47 52 57 59 3 Inhaltsverzeichnis 5.5 SPEC-Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 6 Betriebssysteme 6.1 Allgemeines . . . . . . 6.2 Prozesse und Threads 6.3 Deadlocks . . . . . . . 6.4 Speichermanagement . 6.5 I/O . . . . . . . . . . . 6.6 Betriebssystem-Design 6.7 Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 63 63 69 71 74 74 76 7 Rechnernetze 7.1 Allgemeines . . . . . . . . 7.2 ISO–OSI–Referenzmodell 7.3 Schichtübergreifend. . . . 7.4 Schicht 1 . . . . . . . . . . 7.5 Schicht 2a . . . . . . . . . 7.6 Schicht 2b . . . . . . . . . 7.7 Schicht 3 . . . . . . . . . . 7.8 Schicht 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 79 80 82 83 83 86 88 91 . . . . . . . 8 Rechnernetze II 95 8.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 8.2 Hochleistungsnetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 8.3 Dienstgüte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Literaturverzeichnis III 101 Theoretische Informatik 103 9 Effiziente Algorithmen und Datenstrukturen 9.1 Allgemeines . . . . . . . . . . . . . . . . . . . 9.2 Mathematische Grundlagen . . . . . . . . . . 9.3 Datenstrukturen . . . . . . . . . . . . . . . . 9.4 Selektieren und Sortieren . . . . . . . . . . . 9.5 Minimale Spannbäume . . . . . . . . . . . . . 9.6 Kürzeste Pfade . . . . . . . . . . . . . . . . . 9.7 Matchings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 105 107 108 120 126 127 129 10 Automaten, Formale Sprachen 10.1 Allgemeines . . . . . . . . . . 10.2 Chomsky 3 . . . . . . . . . . 10.3 Chomsky 2 . . . . . . . . . . 10.4 Chomsky 1 . . . . . . . . . . 10.5 Berechenbarkeit . . . . . . . . und . . . . . . . . . . . . . . . Berechenbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 131 132 136 141 142 11 Logik 11.1 Allgemeines . . 11.2 Aussagenlogik . 11.3 Prädikatenlogik 11.4 Beweisideen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 149 149 160 168 Literaturverzeichnis 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 Teil I Praktische Informatik DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 5 Kapitel 1 Datenbanksysteme 1.1 Allgemeines 1.1.1 Was ist der Unterschied zwischen dem relationalem und anderen Modellen? Im Gegensatz zu dem satzorientierten hierarchischen und dem ebenfalls satzorientierten Netzwerkmodell ist das relationale Modell mengenorientiert. 1.1.2 Vergleichen Sie B-Bäume mit Hashing im Kontext von Datenbanken? Beides wird bei Datenbanken eingesetzt. B-Bäume werden zur Speicherung der eigentlichen Daten benutzt, da Sie gut mit Daten, die auf Hintergrundspeichern liegen, umgehen können. Hashing wird bei Indizes gesetzt, da es annähernd O(1) Zugriff erlaubt. 1.1.3 Erklären sie die Begriffe Datenbankschema/Ausprägung und intensional/extensional . Das Datenbankschema legt die Struktur der abgespeicherten Daten fest. Eine Ausprägung ist ein momentan gültiger Zustand der Datenbasis. Man spricht auch von der intensionalen (Schema) und der extensionalen (Ausprägung) Ebene einer Datenbank. Der Unterschied entspricht dem von Klasse und Objekt in der Objektorientierung. 1.1.4 Was ist eine Datenbank? Eine Datenbasis ist eine Menge von Daten. Angereichert um weitere Daten, die das DBMS zur Erfüllung seiner Aufgabe benötigt, bilden sie eine Datenbank. Ein DBMS einschliesslich einer oder mehrerer Datenbanken nennt man Datenbanksystem. 1.1.5 Wie sieht eine relationale Datenbank aus? Eine relationale Datenbank speichert die Daten in Tabellen. 1.1.6 Welche Schichten gibt es bei einer Datenbank? Mann kann drei Schichten unterscheiden. 1. Physische Schicht: Hier wird festgelegt, wie die Daten gespeichert werden. 2. Logische Schicht: Auf der logischen Schicht wird in einem Datenbankschema festgelegt, welche Daten gespeichert werden. 3. Sichten: Durch Sichten werden Teilmengen der gespeicherten Daten bereitgestellt. Das Schichtenmodell dient der Datenunabhängigkeit, es kann zwei Stufen der Datenunabhängigkeit gewährleisten: DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 7 Kapitel 1. Datenbanksysteme Physische Datenunabhängigkeit: Die Modifikation der physischen Speicherstruktur lässt die logische Schicht unverändert. Logische Datenunabhängigkeit: Kleinere Änderungen des Datenbankschemas (z. B. Umbenennung von Attributen) können durch Sichten verborgen werden. 1.1.7 Unterscheiden Sie relationale Datenbanken von objektorientierten Datenbanken. Die Unterschiede entsprechen ziemlich genau den Erweiterungen des objektrelationalen Modells die in 1.8 beschrieben sind. 1.1.8 Erklären Sie den Unterschied zwischen Entity und Klasse. Entitäten haben keine Methoden, entsprechen also eher Pascal Records. Ausserdem sind alle Attribute einer Entität atomar. 1.1.9 Wie kann man Objekte mit einer relationalen Datenbank realisieren? Die Objekte müssen flach geklopft werden, so dass die Objekteigenschaften sich als atomare Attribute speichern lassen. Dazu sind i. A. mehrere Relationen nötig. Methoden können nicht abgebildet werden. 1.1.10 Wo gibt es denn in der Mathematik Schlüssel? Bei Funktionen. 1.1.11 Was ist ein Datenbankschema? Das Datenbankschema legt die Struktur der abgespeicherten Daten fest. 1.1.12 Was ist ein Datenmodell? Ein Datenmodell ist ein Satz von abgestimmten Konzepten zur Beschreibung der Repräsentation von Informationen durch Daten. 1.2 Datenbankentwurf 1.2.1 Was gibt es für Modellierungsmöglichkeiten für Datenbanken? ER-Modell semantisches Modell funktionales Modell objektorientierte Entwurfsmodelle 1.2.2 Beschreiben Sie das ER-Modell. Die grundlegenden Modellierungsstrukturen des Modells sind Entities: Entitäten sind wohl unterscheidbare physisch oder gedanklich existierende Dinge der zu modellierenden Welt. Im ER-Modell wird eigentlich mit Entitymengen gearbeitet. Relationships: Beziehungen zwischen Entitäten. Diese Beziehung können Funktionalitäten habe, z. B. 1:N . Sie können ausserdem durch die (min, max)-Notation noch genauer beschrieben werden. Attribute: Attribute dienen dazu Entitäten zu charakterisieren. Rollen: Rollen spezifizieren die Rolle einer Entität in einer Beziehung, dies ist z. B. bei rekursiven Beziehung nötig. 8 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 1.2. Datenbankentwurf Erweitert wird das ER-Modell durch: schwache Entitäten: Schwache (existenzabhängige) Entitäten sind in ihrer Existenz von einer übergeordneten Entität abhängig. Oft sind sie nur in Kombination mit dem Schlüssel der übergeordneten Entität eindeutig identifizierbar. Generalisierung: Generalisierung bzw. Spezialisierung kann durch is-a-Beziehung modelliert werden. Aggregation: Aggregation kann durch part-of -Beziehungen modelliert werden. 1.2.3 Was ist eine Entität? Entitäten sind wohl unterscheidbare physisch oder gedanklich existierende Dinge der zu modellierenden Welt. 1.2.4 Wie kann man ein ER-Modell in das relationale Modell überführen? Alles was mit dem ER-Modell modelliert wurde, muss (so weit möglich) in Relationen überführt werden. Entitäten: straight-forward! Beziehungen: Zuerst werden alle Beziehungen in Relationen umgesetzt, dies wird später noch verfeinert. Die Relation erhält alle Schlüssel-Attribute der beteiligten Entitäten sowie alle Attribute der Beziehung. Möglicherweise müssen Attributnamen umbenannt werde um Duplikate auszuschliessen oder die Übersichtlichkeit zu erhöhen. Die Wahl der Schlüssel hängt von der Funktionalität der Beziehung ab N :M : Die Menge aller Fremdschlüssel bildet den Schlüssel. 1:N : Als Schlüssel wird der Schlüssel der N Entität gewählt. 1:1: Hier kann man es sich aussuchen. ternäre Beziehungen: Hier müssen die Schlüssel aller nicht-1-Entitäten aufgenommen werden. Generalisierung: Das relationale Modell unterstützt Generalisierung nicht, deshalb gibt es nur suboptimale Lösungen. Mann muss die Information auf die Ober- bzw. Untertypen aufteilen. Dadurch ist aber keine Vererbung realisiert, um an die ganze Information zu gelangen, muss man joinen. Verfeinerung. Das wichtigste ist, dass nur Relationen mit gleichen Schlüsseln zusammengefasst werden dürfen. N :M : Diese Beziehungen bleiben als eigenständige Relation erhalten. 1:N : Diese Beziehungen können zusammengefasst werden, wobei die N -Entität um einen Fremdschlüssel erweitert werden muss. 1:1: Hier kann man sich aussuchen in welche Richtung man zusammenfassen möchte. Das ist natürlich von den Entitäten abhängig, so sollte man z. B. so zusammenfassen, dass möglichst wenige N U LL-Einträge entstehen. 1.2.5 Gibt es Vererbung im ER-Modell? Es gibt Generalisierung, die durch is-a-Beziehung realisiert wird. 1.2.6 Was ist der Unterschied zwischen einem UML-Klassendiagramm und einem ER-Diagramm? Entitäten haben keine Methoden Attribute von Entitäten können nur atomar sein Vererbung wird bei ER durch is-a-Beziehung realisiert Aggregation wird bei ER durch part-of -Beziehung realisiert DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 9 Kapitel 1. Datenbanksysteme 1.2.7 Modellieren Sie eine 3-fach Beziehung. Vorlesungen M Studenten N prüfen 1 Professoren Durch diese Modellierung hat die Relation die Eigenschaften einer partiellen Funktion prüfen : Studenten × Vorlesungen → Professoren D. h. jedem Paar (Student, Vorlesung) ist genau ein Prüfer zugeordnet. Ein Student kann sich also nicht in einer Vorlesung von zwei Professoren prüfen lassen. 1.2.8 Wo kommen im ER-Modell die Fremdschlüssel vor? Die Fremdschlüssel kommen gar nicht vor. Sie existieren implizit durch Angabe der Relationships. 1.3 Das relationale Modell 1.3.1 Was ist ein Schlüssel? Eine minimale Teilmenge von Attributen, deren Werte die zugeordnete Entity eindeutig identifizieren. 1.3.2 Was ist ein Primärschlüssel? Kandidatenschlüssel sind minimale Schlüssel. Gibt es mehrere Kandidatenschlüssel, so wählt man einen als Primärschlüssel 1.3.3 Was ist ein Fremdschlüssel? Attribute einer Relation die dazu dienen Tupel einer anderen Relation zu identifizieren, heissen Fremdschlüssel. 1.3.4 Wie werden Beziehung im relationalen Modell dargestellt? Beziehungen werden wie alles anderen im relationalen Modell durch Relationen (Tabellen) dargestellt. 1.3.5 Geben Sie ein Beispiel für eine Operation auf einer Relation. Selektion (σ) Projektion (Π) Vereinigung (∪) Differenz (−) Kreuzprodukt (×) Umbenennung (ρ) diverse Joins (1) Schnitt (∩) Division(÷) 10 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 1.3. Das relationale Modell 1.3.6 Wie überführt man ternäre Relationships ins relationale Modell? Die Relation erhält alle Schlüssel-Attribute der an der Beziehung beteiligten Entities und alle Attribute der Beziehung. Schlüssel der Relation ist die Vereinigung aller Schlüssel von Entitäten, die eine N oder M Funktionalität haben. 1.3.7 Welche verschiedenen Join-Arten gibt es? Natürlicher Join: Es wird das Kreuzprodukt gebildet und aus diesem nur diejenigen Tupel interpretiert für die gleichnamige Attribute gleiche Werte haben. Durch eine folgende Projektion wird jedes Ergebnis nur einmal aufgenommen. Seien R und S zwei Relationen. Wenn R die Attribute A1 , . . . , Am , B1 , . . . , Bk und S die Attribute B1 , . . . , Bk , C1 , . . . , Cn hat, dann ist der Join wie folgt definiert: R 1 S = ΠA1 ,...Am ,R.B1 ,...,R.Bk ,C1 ,...,Ck (σR.B1 =S.B1 ∧...∧R.Bk =S.Bk (R × S)) Der Join-Operator ist assoziativ und kommutativ. Allgemeiner Join: Beim natürlichen Join werden nur Tupel mit gleichnamigen und gleichwertige Attributen aufgenommen. Beim allgemeinen Join (oder Theta-Join) kann ein beliebiges Prädikat angegeben werden. Somit ist der allgemeine Join nichts anderes als ein Kreuzprodukt gefolgt von einer Selektion: R 1θ S = σθ (R × S) Linker äusserer Join: Die Tupel der linken Argumentrelation bleiben in jedem Fall erhalten. Rechter äusserer Join: Die Tupel der rechten Argumentrelation bleiben in jedem Fall erhalten. Äusserer Join: Die Tupel beider Argumentrelationen bleiben in jedem Fall erhalten. Semi-Join (n): Der Semi-Join von L und R ist definiert als R n S = ΠL (L 1 R) wobei R die Menge der Attribute von R ist. Das Ergebnis enthält also alle Tupel aus R in unveränderter Form die einen potentielle Joinpartner in S haben. Semi-Join (o): analog definiert. Es gilt: RoS =SnR 1.3.8 Erklären Sie Operatoren im Kontext der relationalen Algebra. Im relationalen Modell werden alle Information in Relationen gespeichert. Relationen sind erst mal nur Mengen von Tupeln, deswegen können auf ihnen sämtliche Mengenoperationen durchgeführt werden. Diese Operationen bekommen im Kontext der Datenbankandwendung eine bestimmte Semantik. 1.3.9 Bei welchen Operatoren muss man Duplikate eliminieren? Bei Projektion (Π) und Vereinigung (∪). 1.3.10 Was ist eine Relation? Eine Relation ist eine Teilmenge des Kreuzprodukts mehrerer Mengen. Im relationalen Modell werden Relationen meist als Tabellen dargestellt. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 11 Kapitel 1. Datenbanksysteme 1.4 SQL 1.4.1 Wie würden Sie Vererbung mit Hilfe von SQL nachbauen? Generalisierung kann in SQL durch Sichten imitiert werden. Zum Beispiel möchte man die Spezialisierung von Angestellte auf Professoren und Assistenten darstellen. Dabei bestehen zwei Möglichkeiten: Untertypen als Sicht: Hierbei werden die Untertypen ausschliesslich als Sichten angelegt. Die Anfragen müssen über recht komplizierte Joins aufgelöst werden. Obertypen als Sicht: Die Obertypen werden als Sichten angelegt, die ihre Ergebnisse durch Vereinigung von Anfragen an die Untertypen, berechnen. Die folgenden Beispiele zeigen die Definition der Tabellen und Sichten mit SQL. Untertypen als Sichten Obertypen als Sichten create table Angestellte ( PersNr . . . Name . . . ); create table Professoren ( PersNr . . . Name . . . Rang . . . Raum . . . ); create table ProfDaten ( PersNr . . . Rang . . . Raum . . . ); create table AssiDaten ( PersNr . . . Boss . . . ); create view Professoren as select ∗ from Angestellte a, ProfDaten d where a.PersNr = d.PersNr; create table Assistenten ( PersNr . . . Name . . . Boss . . . ); create view Angestellte as ( select PersNr, Name from Professoren ) union ( select PersNr, Name from Assistenten ); create view Assistenten as select ∗ from Angestellte a, AssiDaten d where a.PersNr = d.PersNr; 1.4.2 Geben Sie Beispiele für SQL-Anfrage. select s.∗ from Studenten s where s.GebDatum < ( select max(p.GebDatum) from Professoren p ); 1.4.3 Wie sieht die allgemeine Syntax einer SQL-Anfrage aus? select A1 , . . . , An from R1 , . . . , Rk where P ; 1.4.4 Wie kann man Anfragen optimieren? Anfrage können optimiert werden indem die entstehende Zwischenergebnisse möglichst klein gehalten werden. D. h. man versucht das unvermeidliche Kreuzprodukt möglichst spät zu machen und es dadurch möglichst klein zu halten. 12 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 1.5. Datenintegrität 1.5 Datenintegrität 1.5.1 Was sind Integritätsbedingungen? Man unterscheidet zwischen statischen und dynamischen Integritätsbedingungen. Statische Bedingungen müssen von jedem Zustand der Datenbank erfüllt sein. Dynamische Bedingungen müssen an Zustandsübergänge sichergestellt werden. Integritätsbedingung sind: Schlüsseldefinitionen, keine zwei Tupel mit gleichem Schlüssel dürfen existieren. Kardinalitäten werden bei dem Übergang ins relationale Modell fest eingebaut. Referentielle Integrität (keine Dangling References) 1.6 Entwurfstheorie (Normalformen) 1.6.1 Geben Sie ein Beispiel für eine Relation, die in 2NF aber nicht in 3NF ist. Ein Verlag speichert alle seine Bücher in der abgebildeten Relation. Diese Relation ist in 2NF da alle nicht Schlüssel-Attribute voll funktional abhängig vom Schlüssel ArtNr sind. Sie ist aber nicht in 3NF da die Nicht-Schlüssel-Attribute Titel und Autor abhängig vom Nicht-Schlüssel-Attribut ISBN sind. ArtNr Titel Autor ISBN Achtung! In der wirklichen Welt ist diese Relation allerdings schon in 3NF. Es gibt nicht für alle Bücher eine ISBN, deshalb existiert die funktionale Abhängigkeit {ISBN} → {Titel, Autor} nicht. 1.6.2 Was ist funktionale Abhängigkeit? Zwei Attributteilmengen α und β von R sind funktional abhängig wenn gilt: Für alle Paare von Tupeln r, t ∈ R mit r.α = t.α gilt auch r.β = t.β. Wenn also eine funktionale Abhängigkeit α → β besteht, müssen für alle Tupel, die in allen Attributen von α gleich sind, auch alle Attribute in β gleich sein. 1.6.3 Was ist ’volle funktionale Abhängigkeit? Die Definition der vollen funktionale Abhängigkeit benötigt man um Schlüssel von Superschlüsseln abzugrenzen. β ist voll funktional abhängig von α wenn gilt: 1. α → β, d. h. β ist funktional abhängig von α und 2. α kann nicht mehr verkleinert werden, d. h. ∀A ∈ α : α − {A} 6→ β Man kann also kein Attribut aus α entfernen ohne die funktionale Abhängigkeit zu zerstören. Wenn β voll funktional abhängig ist von α schreibt man • α → β. 1.6.4 Wann ist eine funktionale Abhängigkeit trivial? Triviale funktionale Abhängigkeiten sind Abhängigkeiten die automatische erfüllt sind. Nur Abhängigkeiten der Form α→β mit β⊆α sind trivial. 1.6.5 Was sind Normalformen? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 13 Kapitel 1. Datenbanksysteme Normalformen dienen dazu die Güte eines Relationenschemas zu bewerten. Es gibt folgende Normalformen: 1NF : Eine Relation ist in erster Normalform wenn alle Attribute atomare Wertebereiche haben. 2NF : Eine Relation ist in zweiter Normalform wenn alle Nicht-Schlüssel-Attribute voll funktional abhängig von allen Kandidatenschlüssel sind. 3NF : Die dritte Normalform ist verletzt wenn eine Nicht-Schlüssel-Attribut von einem anderen Nicht-Schlüssel-Attribut abhängig ist. Eine Relation R ist in dritter Normalform, wenn für jede für R geltende funktionale Abhängigkeit der Form α → B mit α ⊆ R und B ∈ R mindestens eine der drei Bedingungen gilt: B ∈ α, d. h. die funktionale Abhängigkeit ist trivial Das Attribut B ist in einem Kandidatenschlüssel von R enthalten – also B ist prim. α ist Superschlüssel von R. BCNF : Eine Relation R ist in Boyce-Codd Normalform wenn für jede für R geltende funktionale Abhängigkeit α → β eine der folgenden Bedingung gilt: β ⊆ α, d. h. die funktionale Abhängigkeit ist trivial. α ist Superschlüssel von R. 4NF : Eine Relation R mit einer zugeordneten Menge D von funktionalen und mehrwertigen Abhängigkeiten ist in vierter Normalform, wenn für jede mehrwertige Abhängigkeit α →→ β ∈ D + eine der folgenden Bedingungen gilt. Die mehrwertige Abhängigkeit ist trivial. α ist Superschlüssel von R. 1.6.6 Erläutern Sie den Algorithmus zur Zerlegung in 3NF und führen Sie ihn an einem Beispiel vor. Der folgende Algorithmus zerlegt ein Relationenschema R mit funktionalen Abhängigkeiten F so in Relationenschemata R1 , . . . , Rn , dass folgende Kriterien erfüllt sind. R1 , . . . , Rn ist eine verlustlose Zerlegung von R. Die Zerlegung ist abhängigkeitsbewahrend. Alle Ri sind in dritter Normalform. Algorithmus: 1. Bestimme die kanonische Überdeckung Fc zu F . Das geht so: a) b) c) d) Linksreduktion der funktionalen Abhängigkeiten Rechtsreduktion der funktionalen Abhängigkeiten Entfernung funktionaler Abhängigkeiten der Form α → ∅ Zusammenfassung funktionaler Abhängigkeit mit gleichen linken Seiten 2. Für jede funktionale Abhängigkeit α → β ∈ Fc : Kreiere eine Relationenschema Ra := α ∪ β Ordne Ra die funktionalen Abhängigkeiten. Fα := {a0 → β 0 ∈ Fc | α0 ∪ β 0 ⊆ Ra } zu. 3. Falls eines der in Schritt 2 erzeugten Schemata Ra einen Kandidatenschlüssel von R bzgl Fc enthält, sind wir fertig. Ansonsten wähle einen Kandidatenschlüssel κ ⊆ R aus und definiere folgendes zusätzliche Schema: Rκ := κ Fκ := ∅ 4. Eliminiere diejenigen Schemata Ra , die in einem anderen Relationenschema Ra0 enthalten sind, d. h. Ra ⊆ R a0 1.6.7 14 Warum bringt man Relationen in Normalformen? http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 1.7. Transaktionen Bei schlechten Relationenschemata kann es zu folgenden Anomalien kommen Updateanomalie Wenn Informationen redundant gespeichert werden, müssen sie bei einer Änderung an alle Stellen geändert werden. Wenn dies nicht geschieht, treten Problem auf. Selbst wenn dies sichergestellt wird, kostet es Speicherplatz und Performance. Einfügeanomalie Wenn in einem Entwurf Informationen zweier Entitytypen vermischt werden, so ergeben sich Problem wenn man nur eine der Entitäten anlegen will. Die anderen Werte müssen dann mit NULL-Werten aufgefüllt werden. Löschanomalie Wen Information bzgl. eines der beiden vermischten Entitytypen gelöscht werden kommt es zum Verlust von Informationen bzgl. des anderen Typen. Alle diese Anomalien sind darauf zurückzuführen, dass nicht zusammenpassende Information in einer Relation gespeichert werden. Normalisierung soll das verhindern. 1.6.8 Ist die Zerteilung (3NF) verlustlos und abhängigkeitsbewahrend? Ja. Die Relation R wurde in die Relationen R1 , . . . , Rn zerlegt. Mann kann aber alle Information die in der Ausprägung R enthalten war aus den Ausprägungen R1 , . . . Rn rekonstruieren und für R geltenden funktionalen Abhängigkeiten sind auf R1 , . . . , Rn übertragbar. 1.6.9 Was sind die Armstrong-Axiome? Die Armstrong-Axiome sind: Reflexivität: Falls β eine Teilmenge von α ist, dann gilt immer α → β. Insbesondere gilt also immer α → α. Verstärkung: Falls α → β gilt, dann gilt auch α ∪ γ → β ∪ γ. Transitivität: Falls α → β und β → γ gilt, dann gilt auch α → γ. Mit den Armstrong-Axiomen kann die Hülle F + einer Menge F von funktionalen Abhängigkeiten bestimmt werden. 1.7 Transaktionen 1.7.1 Was ist eine Transaktion? Aus der Sicht des Datenbankbenutzers ist eine Transaktion eine Arbeitseinheit, die eine bestimmte Funktion erfüllt. Aus der Sicht des DBMS ist eine Transaktion eine Folge von Lese-und Schreiboperationen abgeschlossen durch ein abort oder commit. 1.7.2 Welche Eigenschaften müssen Transaktionen erfüllen? siehe 1.7.3 1.7.3 Was bedeutet das Akronym ACID? Das Akronym steht für die vier wichtigsten Forderungen an Transaktionen Atomicity: Eine Transaktion muss atomar, also ununterbrechbar sein. Entweder werden keine oder alle Änderungen in der Datenbasis festgeschrieben (alles-oder-nichts-Prinzip). Consistency: Eine Transaktion muss bei Beendigung einen konsistenten Zustand hinterlassen, anderenfalls wird sie komplett zurückgenommen. Während der Ausführung der Transaktion können teilweise Konsistenzbedingungen verletzt werden. Isolation: Nebenläufige Transaktionen dürfen sich nicht gegenseitig beeinflussen. Für eine Transaktion muss es so aussehen, als sei sie die einzige auszuführende Transaktion. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 15 Kapitel 1. Datenbanksysteme Durability: Die Wirkung einer erfolgreich abgeschlossenen Transaktion muss dauerhaft in der Datenbasis festgeschrieben werden. Die bereits ausgeführten Teile einer abgebrochenen Transaktion müssen vollständig rückgängig gemacht werden. 1.7.4 Welche Probleme gibt es bei der Ausführung von Transaktion und welche Lösungen gibt es? Probleme beim Einbenutzerbetrieb: Das DBMS kann abstürzen. Die Recovery muss dafür sorgen, den ursprünglichen Zustand wieder herzustellen. Am Ende einer Transaktion zeigt sich, dass Konsistenzbedingungen verletzt werden. Bisher durchgeführt Operationen müssen Rückgängig gemacht werden. Probleme beim unkontrollierten Mehrbenutzerbetrieb: Lost update: Durch ungeschickte Verzahnung mehrere Transaktionen können Änderungen verloren gehen. Die Mehrbenutzersynchronisation muss das ausschliessen. Dirty read : Durch ungeschickte Verzahnung mehrere Transaktionen liest eine Transaktion Daten, die noch nicht freigegeben wurden. Die Mehrbenutzersynchronisation muss das ausschliessen. Phantomproblem: Eine Transaktion liest während ihres Ablaufs bestimmte Daten zweimal. Zwischen dem ersten und zweiten Zugriff fügt eine andere Transaktion ein neues Datum ein, das mit berücksichtigt werden muss. Dadurch unterscheiden sich der erste und der zweite Zugriff, da beim zweiten Zugriff das neu eingefügte Phantom mit gelesen wurde. Die Mehrbenutzersynchronisation muss das ausschliessen. Probleme beim kontrollierten Mehrbenutzerbetrieb: Verklemmungen: Mehrere Transaktionen können in einen Deadlock geraten. 1.7.5 Was ist Serialisierbarkeit? Beim Konzept der Serialisierbarkeit werden die Vorzüge der seriellen Ausführung (Isolation) mit den Vorteilen des Mehrbenutzerbetriebs (bessere Auslastung) kombiniert. Eine serialisierbare Ausführung mehrer Transaktionen entspricht einer kontrollierten Verzahnung und zwar so, dass das Ergebnis dem des echten seriellen Ausführens entspricht. 1.7.6 Was ist das Serialisierbarkeitstheorem? Das Serialisierbarkeitstheorem besagt, dass eine Historie H genau dann serialisierbar ist, wenn ihr Serialsierbarkeitsgraph SG(H) azyklisch ist. Eine Serialisierung entspricht einer topologischen Sortierung des Serialsierbarkeitsgraphen. Es gibt keine topologische Sortierung, falls dieser Zyklen enthält. 1.8 Objektrelationale und objektorientierte Datenbanken 1.8.1 Was ist eine objektrelationale Datenbank? Das objektrelationale Modell erweitert das relationale Modell wie folgt: Mengenwertige Attribute: Attribute müssen nicht mehr Atomar sein, man erlaubt Mengen als Attribute. Typendeklaration: Das objektrelationale Modell erlaubt das erstellen eigener Datentypen. Referenzen: Attribute können direkt Referenzen auf Objekte haben. Dadurch ist man nicht mehr auf Fremdschlüssel angewiesen. Ausserdem können N :M -Beziehungen durch eine Menge von Referenzen aufgelöst werden. Objektidentität: Voraussetzung für Referenzen ist die eindeutige Identifizierbarkeit von Objekten. 16 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 1.9. Data Warehousing Pfadausdrücke: Wenn Referenzen erlaubt sind, muss die Anfragesprache auch Pfadausdrücke unterstützen. Vererbung Methode: Klassen (bzw. Objekten) können Methoden zugeordnet werden. 1.8.2 Was versteht man unter Objektidentität? Im Gegensatz zu relationalen Modell braucht man beim objektorientierten Modell keine Schlüssel zu verwenden um ein Objekt anzusprechen. Jedes Objekt hat eine systemweit eindeutig Identität, die automatisch generiert wird und sich während der Objektlebenszeit nicht ändert. 1.9 Data Warehousing 1.9.1 Was ist Data Warehousing? Eine Data Warehouse ist eine Datenbank die historische Unternehmensdaten hält, es ist eine OLAPAnwendung (Online Analytical Processing). Der Datenbestand des Data Warehouses wird periodisch mit Informationen aus den operationalen Datenbanken des Unternehmens aufgefrischt. Das Data Warehouse dient der Analyse langfristiger Entwicklungen und daher zur Entscheidungsunterstützung. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 17 Kapitel 2 Compilerbau 2.1 Allgemeines 2.1.1 Geben Sie einen Überblick über die Struktur eines Compilers. Quellprogramm Lexikalische Analyse Token Frontend Syntaxanalyse Syntaxbaum Semantische Analyse Zwischencode Optimierung Zwischencode Backend Codeerzeugung Code lexikalische Analyse: Das Quellprogramm wird mit einem Scanner in eine Folge von Tokens zerlegt. Syntaxanalyse: Aus diesen Token erzeugt der Parser einen Syntaxbaum entsprechende der Syntax der Programmiersprache. semantische Analyse: In der semantischen Analyse wird der Syntaxbaum hinsichtlich semantischer Korrektheit (z. B. Typinformationen) geprüft. Optimierung: In der Optimierungsphase wird der erzeugte Zwischencode optimiert. Codeerzeugung: Aus dem optimierten Zwischencode wird in Maschinencode erzeugt. Der Zielcode kann nochmal optimiert werden. 2.1.2 Welche Fehler werden an welchen Stellen erkannt? lexikalische Fehler : Lexikalische Fehler, z. B. ungültige Zeichen oder ungültige Bezeichner werden vom Lexer erkannt. Syntaxfehler : Der Parser erkennt Syntaxfehler. semantische Fehler : Semantische Fehler, z. B. Typfehler oder ungültige Methodenparameter werden bei der semantischen Analyse erkannt. 2.1.3 In welchen Phasen finden Optimierungen statt? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 19 Kapitel 2. Compilerbau In der Optimierungsphase. Man unterscheidet zwischen der Optimierung von Zwischencode und der Optimierung von Zielcode. 2.1.4 Was sind Attribute? Attribute beschreiben Eigenschaften von Syntaxknoten. 2.1.5 Wie funktioniert die Informationsübergabe zwischen Scanner und Parser? Über Tokens. 2.1.6 Was ist ein Token? Ein Token ist eine Folge von Zeichen die bedeutungsmässig zusammengehören. 2.1.7 Welche Fehler kann das Laufzeitsystem erkennen? Indexüberschreitungen bei Arrays Cast-Exceptions (OO) Null-Pointer-Exceptions 2.1.8 Welche Fehler werden nicht gefunden? Logische Fehler. 2.1.9 Was ist Bootstrapping? Beim Bootstrapping wird ein Compiler für eine neue Programmiersprache Schritt für Schritt entworfen. Zuerst wird in Maschinensprache ein Compiler entworfen, der nur eine Teilmenge der Sprache beschert, mit diesem wird dann ein Compiler geschrieben, der schon etwas mehr kann, usw. 2.2 Lexikalische Analyse 2.2.1 Was ist screenen? Entfernung von Leerzeichen, Zeilenumbrüche, Tabstopps, . . . 2.2.2 Kann man auch Leerzeichen in den Identifaktoren haben? Ja, z. B. bei Fortran. 2.2.3 Was verwendet man zur lexikalischen Analyse? Einen endlichen Automaten. 2.2.4 Wie unterscheidet man Literale von Bezeichnern? Durch Sonderzeichen wie Anführungsstriche. 2.2.5 Dürfen Schlüsselwörter auch Bezeichner sein? Es gibt Sprachen, wo dies erlaubt ist, z. B. PL/1. 20 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 2.3. Syntaktische Analyse 2.3 Syntaktische Analyse 2.3.1 Entwerfen Sie einen Top-Down-Parser für folgende Grammatik: → E E+T | T → T ∗F | F → (E) | id T F wobei E das Startsymbol ist. 1. Elimination der Links-Rekursivität. Diese Grammatik ist links-rekursiv, dass kann zu Endlosschleifen im Top-Down-Parser führen, daher muss die Links-Rekursivität beseitigt werden: E E0 T T0 F → T E0 → +T E 0 | → FT0 → ∗F T 0 | → (E) | id 2. Recursive Decent Parser in Java. Abschnitt 2.7 zeigt eine Java-Implementierung des Parsers. Für jedes Nicht-Terminal existiert eine Methode. E 0 und T 0 heissen in der Java-Version E1 und E2. Statt Bezeichner erkennt der Parser Ziffern. 2.3.2 Was für Sprachen verwendet man und wieso? Man verwendet deterministisch kontextfreie Sprachen, da deren Mächtigkeit annähernd ausreichend ist und das Wortproblem in linearer Zeit gelöst werden kann. 2.3.3 Was ist der Unterschied zwischen LL(k) und LR(k) und was ist mächtiger? Ein LR-Parser erkennt, dass für die Ableitung eines Wortes die Produktion A → β angewandt wurde, nachdem er alles gesehen hat, was aus den Symbolen von β abgeleitet wurde – denn zu jedem Symbol aus β liegt der Ableitungsbaum bereits vor (auf dem Stack) – sowie die nächsten k Zeichen der Eingabe. Ein LL-Parser muss die Entscheidung treffen nachdem er nur die ersten k Zeichen des aus β abgeleiteten Wortes gesehen hat. Daher können LR-Grammatiken mehr Sprachen als LL-Grammatiken beschreiben, es gilt L(LL) ⊂ L(LR). 2.3.4 Was ist eine attributierte Grammatik? Attributierte Grammatiken sind formale Beschreibungen von Übersetzungsprozessen und werden zur Spezifikation und zur Generierung von Übersetzern verwendet. Sie beschreiben, wie die Knoten des erzeugten Syntaxbaums mit Attributen ’dekoriert’ werden, die schliesslich das Übersetzungsergebnis darstellen. Zum Beispiel definiert die Produktion VarDecl↑list = ’var’ NameList↓type↑list ’:’ Type↑type ’;’ folgende semantische Aktionen: NameList.type := Type.type; VarDecl.list := NameList.list; 2.3.5 Was ist eine LALR-Grammatik? Eine LALR-Grammatik kann mit einem LALR-Parser erkannt werden. LALR steht für lookahead LR. LALR stellt einen Kompromiss in Sachen Mächtigkeit und Aufwand zwischen SLR und kanonischer LR da. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 21 Kapitel 2. Compilerbau 2.3.6 Wie kann man Linksrekursionen beseitigen? Um die Linksrekursivität von A zu beseitigen, geht man wie folgt vor: 1. Man teilt alle A-Produktionen in zwei Gruppen auf. rekursiven, in die andere, die nicht-links-rekursiven: A A → → In die eine Gruppe kommen die links- Aα1 | Aα2 | . . . | Aαm β 1 | β2 | . . . | β n 2. Dann ersetzt man die ursprüngliche A-Produktionen durch A → β 1 A0 | β2 A0 | . . . | β n A0 wobei A0 eine neue Variable ist. Für A0 werden folgende Produktionen eingefügt. A → α 1 A0 | α2 A0 | . . . | α m A0 | Wichtig ist die -Alternative. Frage 2.3.1 zeigt ein einfaches Beispiel. Diese Vorgehensweise beseitigt nur direkte Links-Rekursionen. 2.3.7 Welche Fehler erkennt der Parser? Syntaxfehler. 2.3.8 Ist LR(k) mächtiger als LR(1)? Nein. Die Sprachklassen LR(1), LR(2), . . . sind alle gleich mächtig. Es sind die Sprachen, die von einem deterministischen Kellerautomaten über Endzustände erkannt werden. LR(0)-Sprachen werden von einem deterministischen Kellerautomaten über den leeren Keller erkannt. 2.3.9 Erzeugt eine kontextsensitive Grammatik auch einen Baum? Nein. Typ 1 Grammatiken erzeugen Ableitungsgraphen. 2.3.10 Was bedeutet mehrdeutig und welche Probleme sind damit verbunden? Eine Grammatik ist mehrdeutig wenn es für ein Wort mehrere, verschieden Ableitungsbäume gibt. Programmiersprachen sollten nicht mehrdeutig sein, da mehrere Ableitungsbäume meistens auch verschiedenen Bedeutungen entsprechen. 2.3.11 Geben Sie eine Grammatik für arithmetische Ausrücke mit Klammerung, Addition und Multiplikation an. expr expr expr op op 2.3.12 → → → → → expr op expr ( expr ) num + ∗ Welche Fehlerbehandlungsstrategien gibt es? panic mode: Nachdem der Parser einen Fehler gefunden hat, überspringt er folgende Tokens bis zu einem ausgezeichneten Synchronisationstoken, z. B. das Semikolon. phrase level : Der Parser ’behebt’ Fehler in dem er Fehlerhafte Token durch gültige ersetzt, also z. B. eine Komma durch ein Semikolon ersetzt. 22 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 2.4. Semantische Analyse error productions: Falls die Fehler gut bekannt sind, können eigene Produktion für Fehler definiert werden. 2.3.13 Kann jeder Parser nach der Recursive-Decent-Methode implementiert werden? Nein. Die Recursive-Decent-Methode funktioniert nur bei LL-Grammatiken, diese müssen ausserdem frei von Linksrekursionen sein, sonst kann es zu Endlosschleifen kommen. 2.4 Semantische Analyse 2.4.1 Was passiert bei der semantischen Analyse? Die semantische Analyse erfüllt zwei Aufgaben: Überprüfung des Syntaxbaums auf Fehler. Zusätzlich zur kontextfreien Grammatik unterliegen Programmiersprachen noch weiteren, kontextsensitiven Regeln, wie z. B. Bezeichner müssen vor der ersten Verwendung deklariert werden Zuweisungen müssen typkorrekt sein Beim Methodenaufruf müssen Anzahl und Typ der Parameter stimmen Diese Bedingungen müssen während der semantische Analyse überprüft werden, da der (auf kontextfreie Sprachen beschränkte) Parser dies nicht erledigen kann. Vorbereitung der Codeerzeugung. In der semantischen Analyse kann die Codeerzeugung vorbereitet werden, indem z. B. Adressen für Variablen (lokal) vergeben werden. 2.4.2 Was ist eine Symboltabelle? Die Symboltabelle hält für jeden Bezeichner seine Deklarationsinformation. Beim ersten Vorkommen eines Bezeichner wird ein Symboltabelleneintrag angelegt auf den bei späteren Typüberprüfungen zugegriffen werden kann. Symboltabellen werden häufig mit Hashing implementiert. 2.4.3 Welche Arten von Attributen gibt es? Es gibt erzeugte Attribute (synthesized): Attribute werden als erzeugt bezeichnet, wenn der Wert eines Attributs an einem Parsebaum Knoten von Attributen der Kinder dieses Knotens abhängt. Erzeugte Attribute können bei einem einzigen Bottom-Up Durchlauf berechnet werden. vererbte Attribute (inherited): Der Wert von vererbten Attributen wird aus Attributen des Vaterknotens oder von Geschwisterknoten berechnet. 2.4.4 Geben Sie Beispiele für Attribute. Typinformation Code Deklarationsinformation Adressen bzw. Stackinformation 2.5 Codegenerierung und Optimierung 2.5.1 Welche Rolle spielen Attribute bei der Codegenerierung? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 23 Kapitel 2. Compilerbau Während der semantischen Analyse kann bereits Vorarbeit für die Codegenerierung geleistet worden sein, so können bereits Adressen für die Variablen errechnet worden sein. Ausserdem besteht die Möglichkeit, den Code nicht direkt auszugeben, sondern in Codeattributen des Syntaxbaumes zu speichern. Der Wurzelknoten enthält dann den ganzen Code. 2.5.2 Welche Optimierungsmöglichkeit gibt es bei der Codegenerierung Konstantenfaltung: Konstantenausdrücke können zur Compilezeit berechnet werden. Das ist vor allem wichtig für Verbesserungen, die der Programmierer nicht beeinflussen kann. Beispiel Zugriff auf das Arrayelement a[3]: memory[base + offset(a) + 3 · elemSize] ⇒ memory[base + constant] algebraische Vereinfachungen: Teure Operationen durch billige ersetzen, z. B. Division und Multiplikation durch Bit-Shifts. Copy Propagation: Bei Zuweisungen der Form x := y kann in der Folge jedes Vorkommen von x durch y ersetzt werden, solange der Wert von x sich nicht ändert. 3+5·z x := y z := 3 · n := x Hier kann die Variable x weg optimiert werden: y := z := 3·n 3+5·z Entfernung von totem Code: Zum Beispiel Zuweisungen an Variablen, die nicht mehr benutzt werden oder Code der in einem Ast eines If-Then-Statements liegt, das nicht erreicht werden kann. Auflösung von Schleifeninvarianten: Zuweisungen innerhalb einer Schleife, die nicht vom Schleifenzähler abhängen können aus der Schleife herausgezogen werden. Befehlsanordnung: Befehle sollten nach Möglichkeit so angeordnet sein, dass sie das Pipelining moderner Prozessoren unterstützen. Registervergabe: Variablen sollte so auf Register und Hauptspeicher verteilt sein, dass möglichst selten auf den Hauptspeicher zugegriffen wird. 2.5.3 Was ist ’Peephole’-Optimierung? Bei der ’Peephole’-Optimierung wird immer nur ein kurzes Stück Code angeschaut und dort verschiedene Optimierungen durchgeführt. Diese kurze Stück ist das ’Peephole’. Typische Anwendungsfelder für ’Peephole’-Optimierung sind: Entfernung redundanter Anweisungen Kontrollfluss-Optimierung algebraische Vereinfachungen 2.5.4 Wie funktioniert die Registervergabe nach Aho-Johnson? Der Registervergabealgorithmus nach Aho-Johnson arbeitet nach dem Prinzip des dynamischen Programmierens. 2.5.5 Was sind die Ausgangs- und Zieldatenstrukturen? Sowohl Ein- wie Ausgabe der Optimierung ist eine Zwischensprache. Meist verwendet die Zwischensprache einen Drei-Adress-Code der in Tupel gespeichert wird. Diese Zwischensprache ähnelt einer Assemblersprache. 24 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 2.6. Übersetzung objektorientierter Sprachen 2.6 Übersetzung objektorientierter Sprachen 2.6.1 Welchen Unterschiede gibt es zwischen Compilern für objektorientierte Sprachen und Compilern für prozeduralen Sprachen? Entsprechend der Phasen: lexikalische Analyse: keine Unterschiede. Syntaxanalyse: keine Unterschiede. semantische Analyse: Die Typanalyse wird komplizierter, da die Klassenstruktur (Vererbung) berücksichtigt werden muss. Der Compiler muss überprüfen ob Vererbung, Interface-Implementierung, usw. korrekt programmiert ist. Optimierung: siehe 2.6.2. Codeerzeugung: Bei objektorientierten Programmiersprachen kommt es aufgrund der Polymorphie viel öfter zu Fällen, in denen nicht während der Compilezeit gebunden werden kann (dynamische Bindung). 2.6.2 Was für spezielle Methode zur Optimierung gibt es bei OO-Compilern? Man versucht die Bindungen so gut es geht zur Compile-Zeit zu erledigen, dadurch erspart man sich aufwendiges Methoden suchen. Im einfachsten Falle bindet man nur dynamisch, im kompliziertesten macht man eine komplette Datenflussanalyse und versucht damit die Zahl der dynamischen Bindungen klein zu halten. Dies ist aber zu aufwendig. 2.6.3 Erklären Sie an einem Beispiel nochmal genau den Unterschied zwischen dynamischer und statischer Bindung. Wann bindet Java statisch und wann dynamisch? Das Beispiel macht nochmal alles klar. Es gibt eine Klassenhierarchie mit zwei Klassen, Oberklasse ist Shape, Unterklasse ist Circle. Ausserdem gibt es noch eine Klasse Test mit zwei Methoden die je nach Parameter (Shape oder Circle) einen unterschiedlichen String zurückgeben. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 25 Kapitel 2. Compilerbau 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 2.7 class Shape { private String name; public String toString(){ return "Shape"; } } class Circle extends Shape { public String toString(){ return "Circle"; } } class Test { public String print(Shape shape) { return "Test: Shape"; } public String print(Circle circle) { return "Test: Circle"; } In Zeile 29–31 wird eine Variable werden drei Variablen deklariert. shape1 und shape2 haben den Typ Shape. circle hat den Typ Circle. Die Variabel shape1 und circle sind jeweils Objekten zugeordnet, die ihrem statischen Typ entsprechen. Der Variablen shape2 ist ein Kreis zugeordnet, somit eine Instanz einer Unterklasse. Wenn man dieses Programm nun laufen lässt erhält man folgende Ausgabe: Dynamische Bindung Shape Circle Circle Statische Bindung Test: Shape Test: Circle Test: Shape Erst mal zu den Ausgabe die die dynamische Bindung betreffen. Die ersten beiden Ausgabe } sind klar, hier wird einfach die toString()Methode der Klassen Shape bzw. Circle aufpublic class DynBind { gerufen. Die dritte Ausgabe ist nun interespublic static void main(String argv[]) { sant. Obwohl die Variable vom statischen Typ Shape ist, wurde offensichtlich die Methode Test test = new Test(); der Klasse Circle aufgerufen. Die MethodenShape shape1 = new Shape(); bindung hängt also von der aktuellen Instanz Circle circle = new Circle(); ab. Natürlich könnte in diesem einfach Fall Shape shape2 = circle; bereits Compiler die Zusammenhänge erkennen und die Methode statisch binden. Es gibt System.out.println("Dynamische Bindung"); aber viel komplexere Situationen in denen das System.out.println(shape1); tatsächlich nur zur Laufzeit möglich ist oder System.out.println(circle); der Compiler müsste eine sehr aufwendige DaSystem.out.println(shape2); tenflussanalyse machen (und selbst dann geht System.out.println("Statische Bindung"); es nicht immer). Java bindet also die Methoden dynamisch an System.out.println(test.print(shape1)); die Objekte. Der zweite Teil der Ausgabe zeigt, System.out.println(test.print(circle)); System.out.println(test.print(shape2)); dass dies bei den Methodenparametern nicht so } ist. Unabhängig von der Instanz, der die Va} riable zugeordnet ist, wird immer die statische Typdefinition verwendet. Top-Down Parser in Java Dieser Parser arbeitet nicht auf Tokens, die vom Scanner kommen, sondern direkt auf Zeichen. Da in der beschreibenden Grammatik ausserdem -Produktion sind, kommt der Parser nicht mit unbekannten Zeichen zurecht. 26 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 2.7. Top-Down Parser in Java static void nextToken() { lookahead++; } static boolean match(char token) { public class TopDownParser { static char[] input; static int lookahead; if (lookahead >= input.length) return false; static void ntE() throws ParseException { printLA(); System.out.println("E -> T E2"); ntT(); ntE2(); } static void ntE2() throws ParseException { printLA(); System.out.print("E2 ->"); if (match(’+’)) { System.out.println(" + T E2"); ntT(); ntE2(); } else { System.out.println(" e"); } if (token==input[lookahead]) { nextToken(); return true; } else { return false; } } static boolean matchDigit() { if (lookahead >= input.length) return false; if (Character.isDigit(input[lookahead])) { nextToken(); return true; } else { return false; } } static void ntT() throws ParseException { printLA(); System.out.println("T -> F T2"); ntF(); ntT2(); } static void ntT2() throws ParseException { printLA(); System.out.print("T2 ->"); if (match(’*’)) { System.out.println(" * F T2"); ntF(); ntT2(); } else { System.out.println(" e"); } } static void ntF() throws ParseException { printLA(); System.out.print("F ->"); if (match(’(’)) { System.out.println(" ( E )"); ntE(); match(’)’); } else if (matchDigit()) { System.out.println(" digit"); } else throw new ParseException(lookahead); } static void printLA() { System.out.print(lookahead); if (lookahead < input.length) System.out.print ("["+input[lookahead]+"] | "); else System.out.print ("[e] | "); } public static void main(String argv[]) { input = argv[0].toCharArray(); lookahead=0; try { ntE(); System.out.println("k"); } catch (ParseException ex) { System.out.println ("\nParseException at position: " + ex.pos); } } static class ParseException extends Exception { public int pos; public ParseException(int pos){ super("ParseException at position: " + pos); this.pos=pos; } } } } DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 27 Kapitel 3 Software Engineering 3.1 Allgemeines 3.1.1 Was sind Komponenten und wie sind sie definiert? Eine Komponente ist ein physischer und austauschbarer Teil eines Systems der durch eine Schnittstelle spezifiziert ist. Beispiele sind Klassenbibliotheken, Frameworks und Binärprogramme. 3.1.2 Was ist in einer Schnittstelle definiert? Eine Schnittstelle ist ein Übergang zwischen zwei Funktionseinheit mit vereinbarten Regeln für die Übergabe von Daten oder Signalen. 3.1.3 Was wäre den in Java ein Modul und was eine Komponente? Es gibt keine vernünftige Trennung von Modul und Komponente. 3.1.4 Was sind CASE-Tools? CASE steht für Computer Aided Software Engineering. CASE-Tools sind Programm die den Entwickler bei den verschieden Phasen des Engineerings unterstützen. Darüber hinaus unterstützen CASE-Tools auch Bereiche wie Projektkommunikation, Dokumentation,. . . . 3.1.5 Um welche Aspekte geht es beim Software-Engineering? Der Titel des Bruegge Buch sagt es: 3.1.6 Conquering Complex And Changing System . Wie ist der Aufwand dabei verteilt? Man rechnet Ungefähr 10% für Analyse, 20% für Entwurf, 20% für Implementierung und 50% für Testen und Wartung. 3.1.7 Sie haben ein OO-Modell bis runter zu den Objekten, wie geht es dann weiter? Jetzt folgt die Implementierungsphase mit anschliessendem Testen. 3.1.8 Was ist ein Modul? Module sind physische Container in denen man Klassen und Objekte des logischen Designs definiert. 3.1.9 Wie kann man Daten modellieren? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 29 Kapitel 3. Software Engineering Zum Beispiel mit UML oder ER-Diagrammen. 3.1.10 In welche Phasen organisiert man ein Softwareprojekt? Softwareentwicklung besteht normalerweise aus diesen fünf Aktivitäten: Ermittlung der Anforderungen (Requirements Elicitation). Kunde und Entwickler definieren die Aufgabe des Systems. Das Ergebnis kann eine Beschreibung des Systems in Form von Use Case und Akteuren sein. Es werden funktionale so wie nicht-funktionale Anforderung herausgearbeitet. Analyse (Analysis). Das Ziel der Analyse ist es ein Modell aufzustellen das das System komplett beschreibt und alle Anforderung gerecht wird. Bei diesem Schritt befindet man sich noch komplett in der Problemdomäne. Es gibt natürlich Ausnahmen wie Pseudo-Requirements. Wenn der Kunde fordert, dass in Java implementiert wird, ist das eine Anforderung an die Lösungsdomäne. Grobentwurf (System Design). Die Architektur des Systems wird festgelegt, d. h. Designziele werden definiert und das System in kleinere Subsysteme aufgeteilt. Ausserdem wird die Zuordnung von Software auf Hardware festgelegt. Feinentwurf (Object Design). Aufgabe dieser Phase ist es die Kluft zwischen dem Grobentwurf und der Implementierung zu schliessen. Ausserdem werden hier Entscheidung bzgl. dem Einsatz von Fertigprodukten getroffen. Das Ergebnis ist ein detailliertes Objekt Modell mit präzisen Beschreibungen für alle Elemente. Implementation. Jetzt wird das Objekt Modell in Code umgesetzt. Unit-Tests sollten hier stattfinden. Test. Integration- und System Tests. Operation und Wartung 3.1.11 Nochmal Subsystem, Paket, Komponente? Bezüglich der genauen Definition von Subsystem, Paket, Komponente und Modul herrscht alles andere als Einigkeit. UML definiert sie so: Subsystem: Ein Subsystem ist ein Teil eines Gesamtsystems, das nach aussen durch eine Schnittstelle definiert ist und die Struktur und die Zusammenarbeit seiner Einzelteile versteckt. Paket: Ein Paket ist eine lose Ansammlung von Modellelementen, das nicht durch eine Schnittstelle definiert sein muss und sein Inneres nicht kapseln muss. Komponente: Ein Komponente ist eine ausführbare und austauschbare Softwareeinheit mit definierter Schnittstelle und eigener Identität. UML definiert keine Module, aber das dürfte wohl am ehesten den Komponenten entsprechen. 3.2 UML 3.2.1 Wie definiert man Semantik mit der UML? Mit der OCL können Constraints beschrieben werden. 3.2.2 Wenn man eine Software-Architektur mit UML beschreibt, welche Verbindungsrelationen sind da brauchbar? So ziemlich alle Arten von Assoziationen inklusive Aggregation und Kompositionen. 3.2.3 Was ist UML? Die UML ist eine Sprache und Notation zur Spezifikation, Konstruktion, Visualisierung und Dokumentation von Modellen für Softwaresysteme. 30 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 3.3. Ermittlung der Anforderungen (Requirements Elicitation) 3.2.4 Welche Diagrammarten gibt es in der UML? Use Case Diagramme Klassendiagramme Sequenzdiagramme Aktivitätsdiagramme Kollaborationsdiagramme Zustandsdiagramme Verteilungsdiagramme 3.2.5 Vergleiche Sie UML-Klassendiagramme und ER-Diagramme. Entitäten haben keine Methoden Attribute von Entitäten können nur atomar sein Vererbung wird bei ER durch is-a-Beziehung realisiert Aggregation wird bei ER durch part-of -Beziehung realisiert 3.2.6 Gibt es bei Klassendiagrammen auch so etwas wie schwache Entitäten? Schwache Entitäten werden in UML durch Komposition dargestellt. Komposition ist eine strenge Form der Aggregation. Dargestellt wird sie durch eine Linie mit einer ausgefüllten Raute am Ende. 3.3 Ermittlung der Anforderungen (Requirements Elicitation) 3.3.1 Was sind Use Cases? Ein Use Case (Anwendungsfall) beschreibt eine Menge von Aktivitäten eines Systems aus der Sicht seiner Akteure, die für die Akteure zu einem wahrnehmbaren Ergebnis führen. Ein Anwendungsfall wird stets durch einen Akteur initiiert. Ein Anwendungsfall ist eine komplette, unteilbare Beschreibung. 3.3.2 Was sind nicht-funktionale Anforderungen an die Software? Nicht-funktionale Anforderungen beschreiben für den Benutzer wahrnehmbare Aspekte des Systems, die nicht direkt mit dem funktionalen Verhalten des Systems zusammenhängen. Beispiele sind: Performance Reaktionszeit 3.4 Analyse 3.4.1 Wie identifiziere ich Use Cases mit Objekten? Eine Möglichkeit Objekte zu identifizieren ist Natural Language Analysis. Man analysiert die natürlich sprachliche Beschreibung der Use Cases. Dabei kann man nach der Heuristik von Abbott vorgehen. Sie schlägt z. B. folgende Identifikationsregel vor Eigenname → Objekt Substantiv → Klasse Verb → Operation Auxialverb ... sein → Vererbung DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 31 Kapitel 3. Software Engineering 3.5 Grobentwurf (System Design) 3.5.1 Was ist eine Software-Architektur? Eine Software-Architektur ist die Aufteilung eines Softwaresystems in seine Subsysteme, deren Schnittstellen, die Prozesse und Abhängigkeiten zwischen ihnen, sowie den benötigten Ressourcen. Es ist sozusagen ein Blick aus der Vogelperspektive auf das ganze System. 3.5.2 Wie kann man eine Software-Architektur beschreiben? Eine Software-Architektur lässt sich z. B. mit dem 4+1 View Model von Kruchten beschreiben. Diese Modell teilt die Beschreibung in 5 Sichten auf: Logical View : Das Objekt-Modell der Architektur (Grobentwurf). Development View : Diese Sicht bezieht sich auf die tatsächliche Zerlegung des Systems in einzelne Softwaremodule, d. h. die konkrete(n) Zielmaschine(n) werden in diese Betrachtung mit aufgenommen. Process View : Zeigt Nebenläufigkeits- und Synchronisationsaspekte des Systems. Physical View : Beschreibt die Zuordnung von Software auf die Hardware. Deployment View wäre vielleicht ein treffenderer Name. Scenarios: Die Scenarios beziehen sich auf alle Sichten und beschreiben typische Anwendungsfälle des Systems. 3.5.3 Wie kann man Systeme unterteilen? In Subsysteme. 3.5.4 Was ist eine ACL? Eine Access Control List ist mit einem Objekt verknüpft und enthält 2-Tupel (actor, operation) und ordnet damit jedem Benutzer Zugriffsrechte zu. Bei jedem Zugriff wird überprüft, ob der Benutzer für die Operation die entsprechenden Rechte hat. Anschaulich entspricht dies der Gästeliste für eine Party. 3.5.5 Was sind Capabilities? Eine Capability verbindet einen Akteur mit einem 2-Tupel (class, operation). Die Zugriffsinformation wird hier bei den Akteuren gespeichert. Anschaulich entspricht dies einer Party mit Einladungen. 3.5.6 Welche Phasen hat das System Design? Teilaufgaben des System Designs sind: Festlegung der Designziele Zerlegung in Subsysteme Zuordnung von Software auf Hardwarekomponenten Datenmanagement Zugriffskontrollmechanismen Kontrollfluss. Welche Folgen von Operationen werden durchlaufen? Ist das System Event-Abhängig? Boundary Conditions. Start- und Stopp des System, Fehlerbehandlung. 3.5.7 Neben der Trennung von Komponenten gibt es auch Trennung der Schichten, wozu? Reduzierung der Komplexität und Austauschbarkeit. 32 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 3.6. Feinentwurf (Object Design) 3.6 Feinentwurf (Object Design) 3.6.1 Erklären Sie Specification und Implementation Inheritance. Wenn Vererbung nur eingesetzt wird um Code mehrfach zu verwenden, so spricht man von Implementation Inheritance. Wenn Vererbung eingesetzt wird, um durch die Klassen-Subklassen-Hierarchie logisch zu gliedern, spricht man von Specification Inheritance. Implementation Inheritance sollte möglichst vermieden werden, sie kann in vielen Fällen besser durch Delegation ersetzt werden. 3.7 Testen 3.7.1 Was ist ein Regressionstest? Bei jeder Änderung werden alle vorhergehenden Tests wiederholt. 3.7.2 Welche Testverfahren gibt es? Testverfahren lassen sich wie folgt kategorisieren: Unit Testing. Beim Unit Testing werden einzelne Komponenten des Systems getrennt getestet, man unterscheidet zwischen Blackbox Testing. Beim Blackbox Testen testet man Komponenten nur bzgl. ihrer Ein-/Ausgaben. Die Implementierung der Komponenten wird nicht betrachtet. Vorgehensweisen sind: Equivalence Testing. Alle möglichen Eingabe werden in Klassen aufgeteilt. Aus jeder Klasse wird ein Repräsentant ausgewählt mit dem getestet wird. Man geht nun davon aus, dass sich die Komponenten für alle Mitglieder einer Klasse gleich verhält. Für jede Äquivalenzklasse sollten zwei Werte ausgewählt werden, eine typische Eingabe und eine ungültige damit das Fehlerverhalten der Komponente getestet werden kann. Boundary Testing. Boundary Testing is ein Spezialfall von Equivalence Testing. Hier wählt man die Repräsentanten immer so, dass sie an der Grenze zwischen zwei Äquivalenzklassen liegen. Die Idee dabei ist, dass Programmierer Spezialfälle oft stiefmütterlich behandeln. Whitebox Testing. Beim Whitebox Testen schaut man sozusagen in die Komponenten hinein und überprüft deren Implementierung. Eine Möglichkeit ist: Path Testing. Das Path Testing erfordert genaue Kenntnis des Codes der Komponente. Zuerst muss man einen Flussgraphen für die Komponente erstellen, dieser Graph beinhaltet Codeblöcke und Entscheidungen. Dieser kann z. B. als UML Aktivitätsdiagramm dargestellt werden. Jetzt wählt man die Testeingabe so, dass jede Transition des Diagramms mindestens einmal schalten muss. Dies ist aber natürlich immer noch kein Beweis für die Korrektheit, deckt aber normalerweise die meisten Fehler auf. Bei objektorientierten Sprachen wird PathTesting durch Polymorphie und die kürze der Methoden erschwert. State-based Testing. State-based Testing ist eine zustandsorientierte Testmethode speziell für objektorientierte Systeme. Integration Testing. Nachdem die Komponenten einzeln getestet wurden, muss das Zusammenspiel der Komponenten getestet werden. Dabei gibt es wieder verschiedene Ansätze: Big-bang Testing. Alle Komponenten wurden zuvor getestet und werden jetzt auf einen Schlag zusammen gesetzt. Der Vorteil ist, dass man keine zusätzlichen Testumgebungen braucht, der Nachteil das die Fehlersuche sehr aufwendig ist. Bottom-Up Testing. Bei einem schichtenorientierten System werden zuerst nur die Komponenten der untersten Schicht zusammengesetzt und getestet, dann die der nächst höheren usw. Auch hier braucht man keine Testumgebungen. Top-Down Testing. Beim Top-Down Testen geht man genau anders herum vor. Der Nachteil ist, dass man Testumgebungen braucht, die die unteren Schichten simulieren. Der Vorteil ist, dass man mit dem Test des User Interfaces anfängt. Sandwich Testing. Beim Sandwich Testing geht man gleichzeitig von oben und unten vor. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 33 Kapitel 3. Software Engineering System Testing. Mit Unit und Integration Testing sucht man Fehler in einzelnen Komponenten und im Zusammenspiel der Komponenten. Nach der Integration testet man mit dem System Testing, ob das System den funktionalen und nicht-funktionalen Anforderungen entspricht. Functional Testing. Das Functional Testing dient der Überprüfung der funktionalen Anforderungen, die im RAD beschrieben sind. Man hält sich an die beschriebenen Use Cases und geht ähnlich wie beim Equivalence oder Boundary Testing vor. Performance Testing. Performance Testing testet das System auf nicht-funktionale Anforderungen, beschrieben in SDD und RAD. Man unterscheidet zwischen: Stress Testing. Testet ob das System mit auf viele gleichzeitige Anfragen korrekt reagiert. Volume Testing. Testest ob das System mit grossen Datenmengen zurecht kommt. Security Testing. Testet die Sicherheit des Systems. Eingesetzt werden Tiger Teams die Versuchen in das System einzubrechen. Timing Testing. Test ob die Reaktionszeitanforderungen eingehalten werden. Recovery Testing. Testet ob sich das System wie gewünscht von Fehlern erholt. Pilot Testing. Beim Pilot oder Field Testing testen eine ausgewählte Gruppe von Benutzer das System ohne dass ihnen bestimmte Testscenarios vorgegeben werden. Man unterscheidet: Alpha Testing. Pilot Test in der Umgebung der Entwicklung. Beta Testing. Pilot Test in der tatsächlichen Zielumgebung. Acceptance Testing. Der Kunde testet ob das System den Anforderungen entspricht. Man unterscheidet: Benchmark Testing. Der Kunde wählt eine Testmenge mit der System getestet wird. Dies kann entweder durch echte Benutzer oder spezielle Test Teams erfolgen. Competitor Testing. Das System wird mit Konkurrenzsystem oder bei Reengineering Projekten mit dem alten System verglichen. Shadow Testing. Das alte und das neue System laufen parallel und die Resultate werden verglichen. Installation Testing. Das System wird in der Zielumgebung installiert und nochmal getestet. Dabei werden viele Test des Functional und Performance Testings wiederholt. Bestimmte Funktionalität die von anderen System des Kunden abhängig ist, kann erst jetzt wirklich getestet werden. 3.7.3 Wie viel Aufwand entfällt für das Testen? Ca. 40%–50%. 3.7.4 Was versteht man unter Black-Box-Testen? siehe 3.7.2. 3.7.5 Was ist ein Review? Unter einem Review versteht man die Man unterscheidet: händische Analyse des System ohne dieses laufen zu lassen. Walkthrough. Der Entwickler stellt die API, den Code und die Dokumentation dem Review Team vor. Das Team analysiert die Abbildung von der Analyse und des Object Design auf Code bzgl. der Use Cases aus der Analyse Phase und macht entsprechende Vorschläge. Inspection. Bei der Inspection stellt der Entwickler die API dem Review Team vor, er darf aber weder Modelle, Code, Dokumentation, o. ä. vorstellen. Das Review Team prüft dann Code, Dokumentation und auch Kommentare im Code. Diese alles geschieht ohne den Entwickler, der nur bei Unklarheiten herangezogen wird. Code Reviews sind sehr effektiv, so dass in Experiment bis 60%–85% der Fehler gefunden wurden. Sie sind aber auch sehr aufwendig. 34 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 3.8. Projektmanagement 3.7.6 Wie geht man bei Verifikation vor? Verifikation ist der Beweis der Korrektheit eines Programms. Diese ist nur in bestimmten, kleine Fällen möglich. 3.7.7 Testet man in der Analyse Phase? Ja. Man kann Prototypen entwickeln und diese Testen. 3.7.8 Wann testet man? In allen Phasen, aber hauptsächlich während der Implementierung. 3.7.9 Was ist ein Integrationstest? siehe 3.7.2. 3.8 Projektmanagement 3.8.1 Wie kann man ein Projekt aufteilen? Man kann ein Projekt in Aktivitäten aufteilen, die wiederum in Tasks aufgeteilt werden. Jeder Task produziert eine Reihe von Work Products und benötigt eine bestimmte Menge an Ressourcen. 3.8.2 Was versteht man unter Aufwandsschätzung? Man möchte am Beginn des Projektes den Aufwand den Projektes schätzen. Z. B. in Zeilen von Code oder Anzahl der benötigten Entwickler. 3.8.3 Wie ist der Einfluss der Projektdauer auf die Kosten? Bis zu einem gewissen Punkt (z. B. Deadline) ist der Einfluss der Dauer auf die Kosten proportional. 3.8.4 Beschreiben Sie das Cocomo-Modell. In Abschätzung mit dem Cocomo-Modell läuft in etwa wie folgt ab. 1. Komplexität mit Hilfe des Function-Point-Modells bestimmen. Als Ergebnis erhält die man geschätzte Anzahl Codezeilen in KLOC. 2. Einteilung des Systems in eine drei Klassen Organic (einfach), semi-detached (mittel) und embedded (komplex). 3. Entsprechend der Komplexitätsklasse wird nun aufgrund der Anzahl der Codezeilen der Personalaufwand (Personal Effort) in MM (Man-Month) berechnet. einfach: mittel: komplex: PE = 2.4 · KLOC1.05 PE = 3.0 · KLOC1.12 PE = 3.6 · KLOC1.20 4. Anhand dieses Wertes kann nun die Entwicklungszeit in Monaten bestimmt werden: einfach: mittel: komplex: TDEV = 2.5 · PE0.38 TDEV = 2.5 · PE0.35 TDEV = 2.5 · PE0.32 5. Um die Anzahl der Entwickler N zu bestimmen: N= DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs PE TDEV 35 Kapitel 3. Software Engineering Zum Beispiel gilt für ein Projekt mittlerer Komplexität mit geschätzten 30000 Zeilen Code. PE 3.0 · 301.12 = ≈ 135 MM Damit TDEV = ≈ 2.5 · 1350.35 14 M Für die Anzahl der Entwickler ergibt sich. N= 135 ≈ 10 14 Man könnte das System also in 14 Monaten mit 10 Programmierern entwickeln. 3.9 Vorgehensmodelle 3.9.1 Was ist das V-Modell? Das V-Modell ist eine Erweiterung des Wasserfallmodells. Es enthält direkte Verbindungen zwischen Entwicklungs- und Verifikationsphasen. 36 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 3.9. Vorgehensmodelle 3.9.2 Was ist das Spiralmodell? Auch die Weiterentwicklung des Wasserfallmodells durch das flexiblere Spiralmodell bietet benannte und standardisierte Entwicklungsschritte. Diese werden jedoch in einem zyklischen Prozess mehrfach durchlaufen. Das Spiralmodell trägt der Schwierigkeit Rechnung, Anforderungen vorweg zu ermitteln und berücksichtigt den Lernprozess bei der Entwicklung. Ziel ist aber nach wie vor ein fertiges Produkt. Versionen gibt es nur während der Entwicklung. Das wichtigste am Spiralmodell ist, dass es risk-driven ist. Festlegen der Ziele, Lösungsvorschläge, Nebenbedingungen und Einschränkungen Risikoanalye Erarbeitung und Beurteilung von Lösungsvorschlägen, Erkennen und Beseitigen von Risiken Risikoanalye Risikoanalye fo w urf e alys ent rd ob An Entwickulungs- Validierung plan der Analyse Validierung Integrations- und Verfikation plan des Entwurfs Acceptance Test Planung der nächsten Phase Feinentwurf Gr er Anforderungsplan Prototyp 2 Prototyp 3 Prototyp 4 An un gen Prototyp 1 Code Integration & System Test Entwicklung und Validierung des Produkts der nächsten Stufe Jede Runde beinhaltet folgende Aktivitäten. Phase 1 Ziele und Bedingungen festlegen Lösungen vorschlagen Phase 2 Risiken erkennen Risiken beheben Phase 3 Produkt für nächste Runde entwickeln. . . und testen Phase 4 nächste Phase planen DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 37 Kapitel 3. Software Engineering 3.9.3 Gibt es bessere Vorgehensmodelle? Es gibt die zyklische Modelle die immer neue Generationen der Software entwickeln, ein Beispiel ist RUP: Beim Rational Unified Process besteht die Entwicklung aus mehreren Zyklen, am Ende eines jeden Zyklus wird dem Kunden ein Produkt geliefert. Jeder dieser Zyklen besteht aus vier Phasen, wobei jede Phase wiederum in 9 Workflows zerlegt ist. Dadurch wird die Entwicklung sozusagen in zwei Dimension aufgeteilt. Die Phasen: Inception. Hier werden Use Case entdeckt und beschrieben, Risiken und Ressourcenverbrauch abgeschätzt. Elaboration. Die Ideen werden konkretisiert, ein Architekturprototyp aufgestellt und getestet. Dies ist die wichtigste Phase. Construction. In diese Phase finden die Entwicklungsprozesse statt. Transition. In diese Phase fällt die Installation. 3.9.4 Was ist ein Vorgehensmodell und welche kennen Sie? Vorgehensmodelle dienen zur Benennung und Ordnung von produktbezogenen Tätigkeiten bei der Softwareentwicklung. Beispiele sind: Wasserfallmodell V-Modell Spiralmodell RUP Dvorak-Modell 3.9.5 Welche Kritik gibt es am Wasserfallmodell? Im ürsprünglichen Wasserfallmodell werden die Phasen streng sequentiell durchlaufen. Dies entspricht nicht der Realität bei einem Softwareprojekt. Das wird schon daran klar, dass der Wissenserwerb nicht linear abläuft. Eine einzelne Information, auch sehr spät im Entwicklungsprozess, kann alles über den Haufen werfen . Das Wasserfallmodell ist dokumentgesteuert. Anforderungsermittlung Analyse Grobentwurf Feinentwurf Implementierung Test Einsatz und Wartung 3.9.6 38 Vergleichen Sie das Wasserfallmodell mit dem Spiralmodell. http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 3.9. Vorgehensmodelle Jede Runde des Spiralmodells entspricht einer Phase des Wasserfallmodells. Es ermöglicht es flexibler auf Änderungen zu reagieren. Der wichtigste Unterschied zwischen den Modell ist, dass das Spiralmodell risikogesteuert ist. 3.9.7 Steckt in jedem Spiralmodell ein V-Modell? Ja. 3.9.8 Zeichen Sie ein V-Modell auf. Client Acceptance Test Anforderungsermittlung System Test Analyse Integration Test Grobentwurf Unit Test Feinentwurf Implementation 3.9.9 Erklären Sie das Dvorak-Modell. Das Dvorak-Modell erhöht die Flexibilität in dem es in der Entwicklungsphase beliebige Zwischenschritte zulässt. Client Acceptance Test ... ... ... System Test ... Integration Test Anforderungsermittlung Analyse DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs Grobentwurf Feinentwurf Implementation Unit Test 39 Kapitel 4 Objektorientierung 4.1 Allgemeines 4.1.1 Erklären Sie die Merkmale und Konzepte der Objektorientierung. OO Sprachen Kapselung von Daten Instantiierung von Klassen Vererbung Polymorphie OO Systemmodellierung Analyse und Design mit OO Beschreibungstechniken Visualisierung einer Abstraktion des Systems Statische und dynamische Sichten 4.1.2 Was ist Polymorphie? Polymorphie ist die Eigenschaft einer Variable für Objekte verschiedener Klassen stehen zu können. 4.1.3 Was gibt es für Arten von Polymorphie? Ad-hoc Polymorphie. Überladen von Operatoren. Strukturelle Polymorphie. Ein Variable kann für Objekte verschiedener Klassen stehen, wenn sie eine gemeinsame Oberklasse haben. Parametrische Polymorphie. Wird zum Beispiel bei parametrischen Klassen verwendet. 4.1.4 Was sind Vorteile der Polymorphie? Vereinfacht die Programmierung. 4.1.5 Was sind Nachteile der Polymorphie? Polymorphie muss oft durch dynamisches Binden gelöst werden, das geht auf die Performance. Oft wird schlechtere Lesbarkeit des Codes als Nachteil angegeben. Das ist Blödsinn. Wenn man statt Polymorphie haufenweise verschachtelte Instance-Of-Abfragen verwendet, wird der Code sicher nicht besser lesbar. 4.1.6 Was gibt es für Notationen? Booch-Notation und UML. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 41 Kapitel 4. Objektorientierung 4.2 Entwurfsmuster 4.2.1 Was sind Entwurfsmuster? Softwareentwickler haben bei der Analyse grosser Softwaresysteme festgestellt, dass es für ähnliche Problemstellungen gleiche Konfigurationen von Entwurfskomponenten gefunden wurden, d. h. Klassen oder Objekte die eine bestimmte Dienstleistung erbringen. Solche Lösungen lassen sich als Muster beschreiben und benennen und bilden damit die Grundlage für Sammlungen zugehöriger Musterbeschreibungen. 4.2.2 Erklären Sie das Decorator-Pattern. Das Decorator-Pattern erlaubt es die Funktionalität von Objekten zur Laufzeit zu erweitern. Es bietet oft eine flexible Alternative zur Vererbung. 4.2.3 Erklären Sie das Observer-Pattern. Mit dem Observer-Pattern kann eine 1:N Beziehung zwischen Objekten realisiert werden, so dass die N Objekte (Observer) geupdatet werden, wenn das eine Objekt (Subject) seinen Zustand ändert. Dies wird z. B. bei MVC eingesetzt. Bei Java Swing heissen die Observer Listener. 4.2.4 Erklären Sie das Adapter Pattern. Eine Adapterklasse kann man eine andere Klasse Schnittstelle hat. 4.2.5 vorschalten, wenn diese nicht die gewünschte Erklären Sie das Facade Pattern. Mit dem Facade Pattern kann man den Zugriff aus ein ganzes Subsystem vereinfachen. Dem Subsystem wird eine Facade-Klasse hinzugefügt, die die ganze Kommunikation mit anderen Subsystemen abwickelt. 4.2.6 Erklären Sie das Bridge Pattern. Mit dem Bridge-Pattern kann die Implementierung und die Abstraktion einer Klasse in zwei unterschiedliche Klassenhierarchien aufgeteilt werden. Dadurch können die beiden unabhängig voneinander geändert werden. 4.2.7 Erklären Sie das Strategy Pattern. Mit dem Strategy Pattern lassen sich einzelne Algorithmen einer Klasse getrennt austauschen. So kann man z. B. in eine Klasse die Graphen darstellt verschiedene Layoutalgorithmen einhängen. 4.2.8 Erklären Sie das Visitor Pattern. Mit dem Visitor-Pattern können Operationen auf Klassen selbst wieder in Klassen gepackt werden. Dadurch können Operationen für Klassen definiert werden ohne das die Klassen selbst geändert werden müssen. Das ist speziell bei generierten Klassen interessant. Das Visitor-Pattern wird häufig bei der semantischen Analysephase im Compilerbau eingesetzt. 4.2.9 Erklären Sie das Command Pattern. Das Command Pattern ist ein objektorientierter Ersatz für Call-Back-Methode. Es erlaubt einzelne Operationen dynamisch in Objekte einzuhängen. Das Actionkonzept von Java Swing ist eine Anwendung des Command Patterns. 42 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 Literaturverzeichnis [Aho et al., 1986] Aho, Alfred V. et al. Compilers: Principles, Techniques and Tools. AddisonWesley, 1986 [Duden Informatik, 1993] Duden Informatik. 2. Aufl. Dudenverlag, 1993 [Bauer & Höllerer, 1998] Bauer, Bernhard; Höllerer Riitta. Übersetzung objektorientierter Programmiersprachen. Springer-Verlag, 1998 [Bruegge & Dutoit, 2000] Buegge, Bernd; Dutoit, Allen H. Object-Oriented Software Engineering: Conquering Complex and Changing Systems. Prentice Hall, 2000 [Gamma et al., 1994] Gamma, Erich et al. Desgin Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994 [Oestereich, 2001] Oestereich, Bernd Die UML-Kurzreferenz für die Praxis. Oldenbourg, 2001 [Kemper & Eickler, 1999] Kemper, Alfons; Eickler, André Datenbanksystem: Eine Einführung. Oldenbourg, 1999 [Rechenberg & Pomberger, 1999] Rechenberg, Peter; Pomberger, Gustav (hrsg.) Informatik-Handbuch. 2. Aufl. Hanser, 1999 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 43 Teil II Technische Informatik DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 45 Kapitel 5 Rechnerarchitektur 5.1 Allgemeines 5.1.1 Erläutern Sie das von-Neumann-Konzept. Beim von-Neumann-Konzept gelten folgende Prinzipien: 1. Der Computer besteht aus Speicher Rechenwerk Leitwerk Ein-/Ausgabegeräten. Heute: Aufspaltung des Speichers in Speicherhierarchie Leitwerk und Rechenwerk formen Zentraleinheit gelegentliche mehrere Rechenwerke im Zentralprozessor Ein-/Ausgabegeräte über Gerätesteuerungen indirekt angeschlossen Verbindungsnetz ist kritischer Teil 2. Die Struktur des Rechners ist unabhängig vom bearbeiteten Problem. Heute: gilt immer noch. 3. Programm und Daten stehen im selben Speicher und können beide durch die Maschine verändert werden. Heute: Manchmal liegt das Programm in einem Festspeicher, z. B. bei Taschenrechnern, aber in Normalfall wie von-Neumann, aber getrennt in unveränderliche Teile (pure segments) und veränderliche Teile (impure segments). Die Möglichkeiten der Veränderung des Programms wurde durch Sprünge und Adressmodifikation abgelöst. Das ist sicherer und erlaubt Programme als pure segments. 4. Der Speicher ist Zellen gleicher Grösse geteilt, die durch fortlaufende Nummern bezeichnet sind. Heute: Gilt noch immer, aber die untypisierten, unstrukturierten Zellen sind Hauptangriffspunkt von von-Neumann-Kritikern. 5. Das Programm besteht aus einer Folge von Befehlen, die sequentielle Aufträge beschreiben und in der Aufzeichnungsreihenfolge auszuführen sind. Die Befehle enthalten i. A. nicht die Werte, mit denen gerechnet werden soll, sondern die Adresse der Zellen, wo die Werte liegen. Heute: Heutige Prozessoren bemühen sich mit allen möglichen Tricks (Pipelining, look-aheadMechanismen, mehrere Rechenwerke) die Sequentialität zu umgehen. Trotzdem muss das Ergebnis dem der sequentiellen Verarbeitung entsprechen. 6. Von der Folge kann durch Sprungbefehle abgewichen werden. Die Ausführung eines Sprungs kann von errechneten Werten abhängig gemacht werden. Heute: praktisch unverändert. 7. Die Maschine benutzt Binärcodes, Zahlen werden dual dargestellt. Heute: praktisch unverändert. Abbildung zeigt original von-Neumann-Modell: DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 47 Kapitel 5. Rechnerarchitektur Befehle (Programme) Hauptspeicher Rechenwerk Daten Programm Daten Leitwerk BZ IR SR AC Adressen Daten E/A-Werk 5.1.2 Welche von-Neumann-Einheiten kommunizieren heutzutage miteinander und wie? Der Prozessor ist über den Prozessor-Speicher-Bus mit dem Hauptspeicher verbunden. Auch mit den Peripheriegeräten kommuniziert er über Busse. Die Abbildung zeigt eine typische Bushierarchie. Prozessor Secondary Cache Hauptspeicher E/ASteuerung für langsame Geräte, z.B. Bänder Peripheriebus Prozessor-Speicher-Bus Bussteuerung Busbrücke für schnelle Geräte, z.B. LAN E/ASchnittstelle Systembus 5.1.3 Wie erweitern heutige Architekturen das von-Neumann-Konzept? Adressmodifikation: die Programmadresse wird zuerst in eine Prozessadresse und dann in eine Maschinenadresse umgesetzt. Gleichzeitige Bearbeitung in mehreren weitgehend unabhängigen Werken: Zentralprozessoren, Rechenwerke, Kanäle, Peripheriegeräte,. . . Automatisch verwaltete Speicherhierarchie. Steuerung nicht durch ein Programm sondern eine Hierarchie von Steuervorgängen. Programmunterbrechung durch interne und externe Signale. 5.1.4 Was für Klassifikationssyteme kennen Sie? 1. Klassifikationssystem von Flynn, klassifiziert nach: SI vs MI : ein oder mehrere (durch die Rechnerhardware) gleichzeitige Progamminterpretationen SD vs MD: ein oder mehrere (durch die Rechnerhardware) gleichzeitige Operationen auf Daten Dadurch ergeben sich Kombinationen wie SISD (Standard), SIMD (Vektor-/Feldrechner), MIMD (Multiprozessoren) 48 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.1. Allgemeines 2. Erlangen Classification System (ECS): Rechner werden durch Tripel von Paaren beschrieben (k ∗ k 0 , d∗ d0 , w∗ w0 ). Kollateralität der Programminterpretation: k Zahl der Leitwerke bzw. Prozessoren k 0 Zahl der (hardwareunterstützt) im Pipelining zusammen wirkenden Programminterpretationen Kollateralität der Befehlsausführungen je Programminterpretation: d Zahl der gleichen Rechenwerke je CPU d0 Zahl der verschiedenen Rechenwerke je CPU Kollateralität in Ausführung eines Befehls w Operandenwortlänge w0 Zahl der Pipelinestufen Beispiele: Intel 8085 Multirechner aus 128 Intel 80386 Cray.1 Vektorrechner 5.1.5 (1∗ 1, 1∗ 1, 8∗ 1) (128∗ 1, 1∗ 1, 32∗ 1) (1∗ 1, 1∗ 12, 64∗ 14) Was ist SPEC? Die Standard Performance Evaluation Corporation (SPEC) unterhält als Dachorganisation diverse Unterabteilungen, die sich mit Grafik (GPC) oder High Performance Computing (HPC) beschäftigen und nicht zuletzt gibt es die Open System Group OSG, die für Komponenten und Systeme im Workstationund Multiuser-Serverbetrieb zuständig ist. Diese OSG hat diverse Benchmarks entwickelt beziehungsweise zusammengestellt, zum Beispiel für Web-Server (SPEC WEB99), Mail-Server (SPEC MAIL2001), Java Virtual Maschines (SPEC JVM98), Java Business (SPEC JBB2000) – aber am bekanntesten ist ihre CPU-Benchmark-Suite SPEC CPU2000. Aber nicht nur in der multikulturellen Zusammensetzung des Benchmark-Komitees, sondern auch in vielen anderen Punkten unterscheidet sich der CPU2000-Benchmark der SPEC von normalen Benchmarks. Insbesondere liegt die Suite als Source (C, C++, Fortran) vor, sodass man weiß, was der Benchmark so treibt und es der Tester durch die Wahl der Compiler und Optimierungsflags in der Hand hat, ob der Benchmark mehr altbacken arbeiten oder das Potenzial der modernen Prozessoren mit all ihren neuen Funktionseinheiten voll ausschöpfen soll. Die Suite besteht aus zwei Teilen, dem Gleitkommabereich (SPECfp), der eine Fülle wissenschaftlicher Software umfasst und überwiegend in Fortran kodiert ist, und dem Integer-dominierten Bereich (SPECint), kodiert in C/C++. SPECint enthält auch viele typische Desktop-Aufgaben, etwa Komprimierung (gzip und bzip2), Kompilation mit gcc, auch ein Schachspiel (crafty) oder Sprachanalyse (parser). SPECint spiegelt damit wesentlich mehr die normale Desktop-Anforderung wider und eignet sich daher eher für ein Speed rating von Desktop-Prozessoren. Das gleiche gilt für Server, für die Gleitkommaleistung eher nebensächlich ist. Auch hier zählt vor allem der SPECint-Wert. Zwei verschiedene Messverfahren erlaubt die SPEC in ihren restriktiven Runrules. Bei Base muss jede der beiden Einzelsuites mit einem Compiler und einem gemeinsamen Satz von höchstens vier Optimierungsflags verwendet werden, bei Peak darf man für jedes einzelne der insgesamt 26 Programme unterschiedliche Compiler mit beliebigen Flags mischen. Diese Zusammenfassung stammt aus der c’t 21/01. Für jeden der einzelnen Test, 12 bei SPECint und 14 bei SPECfp, wird der sog. SPEC-Ratio berechnet. Der SPEC-Ratio ist der Kehrwert der Laufzeit geteilt durch den Kehrwert einer Referenzlaufzeit. Aus den SPEC-Ratio-Werte wird dann mit dem geometrischen Mittel ein Mittelwert bestimmt, der SPECMark. 5.1.6 Kennen Sie noch andere Rechner ausser Workstations? Taschenrechner PDAs DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 49 Kapitel 5. Rechnerarchitektur Spielkonsolen PCs und Laptops Servers Mainframes Supercomputer 5.1.7 Welche Bedeutung haben Angaben wie SPEC-Werte, MIPS oder MFLOPS? MIPS (Million Instructions per Second ) und FLOPS (Million Floating Point Operations per Second ) sind Durchsatzkenngrössen. SPEC-Werte sind die Ergebnisse der standardisierten SPEC-Benchmarks. MIPS- und FLOPS-Werte werden häufig falsch interpretiert. Vergleiche zwischen Rechnern aufgrund dieser Werte sind irreführend, wenn diese unterschiedliche Befehlssätze oder Hardwareunterstützung für Gleitpunktoperationen haben. Eine RISC-System hat meist einen höheren MIPS-Wert als ein CISCWert, das sagt aber noch überhaupt nichts aus, da die Mächtigkeit der Befehle sehr unterschiedlich ist. Ein weiteres Problem ist, dass die Werte insofern keine verlässlichen Aussagen machen, da sich moderne Prozessoren sich in wirklichen Einsatz die meiste Zeit mit Pipelinekonflikten und dem Warten auf den Speicher beschäftigen und nicht mit Gleitpunktberechnungen. SPEC-Werte haben einen eine deutlich höhere Aussagekraft, da durch Standardisierung und Mittelung das erste Problem beseitigt ist. Rechensysteme lassen sich damit tatsächlich vergleichen. Aber nur bzgl. ihrer SPEC-Werte. Der SPEC-Benchmark ist synthetisch und es ist nicht gesagt, dass die erzielten Werten etwas über die Leistungsfähigkeit eines Systems bei seiner realen Aufgabe aussagen. 5.1.8 Was kann man bei Rechnern bewerten und was für Methoden gibt es dafür? Die bei Rechnern zu bewertenden Kenngrössen lassen sich in drei Bereich aufteilen. Zeit. Die Verweilzeit ist die Zeit, die ein Auftrag insgesamt im System verbringt. Die Verweilzeit setzt sich zusammen aus Wartezeit und Bedienzeit. Die Verweilzeit wird auch als Verzögerung, Antwortzeit oder Latenz bezeichnet. Die Zeiten sind meist stochastische Grössen, im Allgemeinen interessiert aber nur der Erwartungswert. Beispiele sind: Befehlsausführungszeit (CPI), Speicherzugriffszeit, Laufzeit eines Programmes, Verzögerung einer Nachricht in einem Netz. Durchsatz. Der Durchsatz ist definiert als die Anzahl der pro Zeiteinheiten bedienten Aufträge. Der maximale Durchsatz heisst Grenzdurchsatz. In den meisten Fällen sind die Antwortzeiten für System, die in der Nähe des Grenzdurchsatzes operieren, sehr hoch. Deshalb interessiert meist der maximal erzielbare Durchsatz, so dass eine obere Schranke für die Verweilzeit nicht überschritten wird. Das nennt man Usable Capacity. Die Verlustrate ist die Anzahl von Aufträgen, die pro Zeiteinheit verloren geht. Beispiele sind: MIPS und FLOPS eines Prozessors, Bit/s auf einem Bus oder Netz, ausgeführte Jobs pro Stunde, Zellverlustrate bei ATM. Auslastung. Unter der Auslastung eines System versteht man das Verhältnis von tatsächlich erreichtem Durchsatz zum Grenzdurchsatz, dies wird manchmal auch als Effizienz bezeichnet. Bei einem zusammengesetzten System, stellt das System mit der höchsten Auslastung den Bottleneck dar. Zur Bewertung der obigen Kenngrössen bietet es sich an das System als einfaches Warteschlangensystem zu modellieren. Deshalb gilt auch der Satz von Little: U = λ · Tr wobei U die mittlere Anzahl der Aufträge im System, λ die Ankunftsrate und Tr die mittlere Verweilzeit bezeichnet. Zur Bestimmung dieser Kenngrössen gibt es verschiedene Verfahren, die sich in drei Gruppen gliedern lassen. Analytische Modelle und Verfahren. In analytischen Modellen wird das zu bewertenden System in Form von Gleichung beschrieben. Verwendet werden einfache Formeln, Warteschlangenmodelle, Petri-Netze. Speziell bei den Warteschlangenmodellen stehen mächtige Methoden zur Bewertung zu Verfügung. Generell gilt aber, dass der Entwurf von analytischen Modellen mit zunehmender Komplexität der Systeme immer schwieriger bis praktisch unmöglich wird. 50 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.1. Allgemeines Simulation. Bei der Simulation werden Struktur und Verhalten des Systems in einem geeigneten Abstraktionsgrad mit in einem ausführbaren Rechnermodell nachgebildet. Man unterscheidet zwischen stochastischer und deterministischer Simulation. Bei der stochastischen Simulation werden die Modelle mit zufälligen Daten gefüttert. Bei der deterministischen Simulation verwendet man Daten die aus Experimenten mit realen System gewonnen wurden. Messung. Eine weitere Methode ist, Messungen an existierenden System durchzuführen. Man unterscheidet zwischen Monitoring und Benchmarking. Monitoring dient dazu interessante Ereignisse, Operationen und Zustände von Systemen aufzuzeichnen. Ziel ist es das Verhalten des Systems zu verstehen und Engpässe zu identifizieren. Ausserdem können Werte gewonnen werden, die bei den deterministischen Simulationen eingesetzt werden. Benchmarking wird in 5.1.9 genauer beschrieben. Validierung. Wichtig ist eine fundierte Validierung der Modelle. 5.1.9 Was ist Benchmarking? Benchmarks sind standardisierte Messprogramme zur Ermittlung der Rechnerleistung. Es wird nicht nur die Leistung der Hardware, sondern des Gesamtsystems gemessen. D. h. Compiler und Betriebssystem werden mit bewertet. Es gibt Benchmarks die im Quellcode vorliegen und sog. Paper-and-Pencil Benchmarks. Bei diesen ist nur die Aufgabe spezifiziert, die Lösung muss selbst implementiert werden. Man unterscheidet zwischen synthetischen und natürlichen Benchmarks. Synthetische Benchmarks. Man benutzt Bausteine, die jeweils eine gewünschte Teilbelastung erzeugen und im Hinblick auf die Gesamtleistung zu einem Benchmark komponiert werden. Beispiele sind Whetstone und SPEC (siehe 5.1.5). Natürliche Benchmarks. Bei natürlichen Benchmarks benutzt man als repräsentativ angesehene echte Programme zur Leistungsmessung. Beispiele sind Benchmarks die z. B. PCs mit Windows und typischen Office-Anwendungen testen. 5.1.10 Definieren Sie die Begriffe Zuverlässigkeit und Verfügbarkeit. Unter der Zuverlässigkeit R(t) versteht man die Wahrscheinlichkeit eine Systems das Zeitintervall [0, t] zu überleben, unter der Voraussetzung, dass es zum Zeitpunkt t = 0 funktionsfähig war. Da das System zu Beginn des Zeitintervalls funktionsfähig ist, gilt R(0) = 1. Ausserdem geht man davon aus, dass das System irgendwann ausfällt. lim R(t) = 0 t→∞ Die Ausfallrate wird mit λ(t) bezeichnet: λ(t) = − dR(t) /R(t) dt Wenn man von einer konstanten Ausfallrate ausgeht, so gilt für die Zuverlässigkeit: R(t) = e−λt Die mittlere Zeit bis zu einem Fehler (MTF) berechnet sich dann so: Z ∞ 1 MTF = R(t) dt = λ t=0 Die Annahme, dass λ konstant ist, gilt für technische Systeme im Normalbereich, d. h. Frühausfälle hat es bereits hinter sich gelassen und Spätausfälle noch nicht erreicht. Bei der Zuverlässigkeit unterscheidet man nur zwischen den Zuständen läuft (1) und läuft nicht (0). Bei reparierbaren System interessiert weiterhin der Begriff der Verfügbarkeit. Die Verfügbarkeit A(t) ist Wahrscheinlichkeit, dass das System zu einem Zeitpunkt t in einem funktionierenden Zustand angetroffen wird. Die stationäre Verfügbarkeit A = lim A(t) t→∞ DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 51 Kapitel 5. Rechnerarchitektur gibt den zu erwartenden Bruchteil der Zeit an, den das System zur Verfügung steht. Zum Reparieren des System braucht man eine durchschnittliche Zeit MTR, den Kehrwert davon bezeichnet man als Reparaturrate und nennt ihn µ. Wenn ein System eine konstante Ausfallrate und eine konstante Reparaturrate hat, dann gilt für die Verfügbarkeit: A= MTF µ = λ+µ MTR + MTF 5.2 Zentralprozessoren 5.2.1 Wie sieht der Maschinenbefehlszyklus aus? 1. Befehlsholphase (BH ). Nächsten Befehl ins Instruktionsregister laden. 2. Decodierungsphase (DE ). Interpretation des Maschinenbefehls. 3. Operandenholphase (OP ). Den ALUs die Operanden zur Verfügung stellen. 4. Ausführungsphase (AU ). Befehl ausführen. 5. Rückschreibephase (RS ). Ergebnis in Register oder Speicher ablegen. 6. Adressierungsphase (AD). Befehlszähler fort schalten, evtl. Springen. Der klassische Maschinenbefehlszyklus von RISC-Maschinen sieht so aus: 1. Instruction Fetch. Nächsten Befehl holen und Befehlszähler fort schalten. 2. Instruction decode/register fetch. Befehl dekodieren und Register lesen. Bei Verzweigungsbefehle kann bereits in dieser Phase das eventuelle Sprungziel bestimmt werden. 3. Execution/effective address. In Abhängigkeit vom Befehl führt die ALU eine der drei Funktionen aus. Memory reference. Die ALU berechnet Prozessadresse mit Basisregister und Offset. Register-Register ALU Instruction. Die ALU führt die entsprechende Berechnung durch, verwendet werden die aus den Register geladenen Wert. Register-Immediate ALU Instruction. Die ALU führt die entsprechende Berechnung durch. 4. Memory Access. Falls es sich um einen Load/Store-Befehle handelt, wird hier der Speicher geschrieben, bzw. gelesen. 5. Write Back. Ergebnisse der Berechnung oder des Loads werden in die Register zurückgeschrieben. 5.2.2 Was ist der Unterschied zwischen RISC und CISC? CISC-Architekturen haben folgende Eigenschaften: Umfangreiche Befehlssätze mit einigen hundert Befehle, die Teilweise komplizierte Aufgaben lösen viele Adressierungsarten mit Register-, Speicher- und Keller-Zugriffsarten und Adressrechnung in Maschinenbefehlen Familienkonzept bei aufwärtskompatiblen Prozessoren mit grossen Leistungsunterschieden Nutzung von Mikroprogrammierung für das Leitwerk Trennung von Befehlssatz, Implementierung und Realisierung Befehlssätze sind möglichst speichersparend entworfen, hauptsächlich durch uneinheitliches Befehlsformat Im Gegensatz dazu zeichnen sich RISC-Architekturen durch folgende Eigenschaften aus. elementare, kleine Befehlssätze, deren Operandenhol- und Ausführungsphase in je einem Takt durchführbar ist Verzicht auf Adressierungsarten und Adressrechnung in der Operandenholphase Operanden ausschliesslich in Registern (Load-Store-Architektur ) und grosse, universelle Registersätze 52 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.2. Zentralprozessoren Nutzung von Pipelining fest verdrahtete Leitwerke (= keine Mikroprogrammierung) einheitliches Befehlsformat Kooperation von optimierenden Compiler und der Prozessorarchitektur zur Leistungssteigerung 5.2.3 Welche Arten von Interrupts gibt es? Interrupts lassen sich nach vielen Kriterien klassifizieren, z. B. Software vs. Hardware, Maskable vs Non-Maskable, Precise vs Imprecise. 5.2.4 Wer kann welche Interrupts auslösen? Kommt auf den Interrupt an. Arithmetische Interrupts werden vom Rechenwerk ausgelöst Seitenfehler von der MMU Hardwareinterrupts z. B. von der Tastatur ... 5.2.5 Was ist Pipelining? Idee beim Pipelining ist es die Befehle wie am Fliessband abzuarbeiten. Parallelismus in der Ausführung und damit Leistungssteigerung entsteht beim Pipelining dadurch, dass das erste Teilwerk nach Bearbeitung des i-ten Befehls bereits mit dem Befehl i + 1 fortfahren kann. Bedingung ist, dass für alle Phasen des Maschinenzyklusses ein eigenes Werk zur Verfügung steht. Die Abbildung zeigt eine 6-Stufige Pipeline. Befehl 1 2 3 4 BH DE OP AU BH DE OP BH DE BH RS AU OP DE AD RS AD AU RS AD OP AU RS AD t Eine Pipeline kann nur effektiv Arbeiten wenn die einzelnen Phasen des Maschinenzyklus in etwa gleich lang dauern. Bei modernen Prozessoren unterscheiden sich die Befehle speziell in der Ausführungsphase oft recht stark, deshalb werden die einzelnen Phasen des Befehlszyklus hier nochmal in mehrere Phasen zerlegt. Um jede Phase des Befehlszyklus’ möglichst in einem Takt auszuführen, werden verschiedene Techniken in den unterschiedlichen Phasen eingesetzt: 1. Befehlsholphase. Die Befehlsholphase kann durch den Einsatz von Caches und weiteren Befehlspuffer die logisch zwischen Cache und dem Instruktionsregister liegen, beschleunigt werden. 2. Decodierungsphase. Die Beschleunigung der Decodierphase erfolgt hauptsächlich durch Vereinheitlichung und Vereinfachung des Befehlssatzes. Wesentliche Voraussetzung für das einheitliche Befehlsformat ist die ausschliessliche Verwendung von Registeroperanden. Dadurch müssen bei der Dekodierung keine aufwendigen Adressberechnungen vorgenommen werden. 3. Operandenholphase. Bei RISC-Architekuren ist die Operandenholphase sehr einfach, da nur Register als Operanden verwendet werden. Weiter Vereinfachung ergibt sich dadurch, dass keine Statusregister verwendet werden. Auch die Verwendung des Dreiadressprinzips hilft, da das überschreiben von Operanden entfällt. Durch diese und andere Eigenschaften von RISC-Architekturen wird die Operandenholphase so einfach, dass sie zusammen mit der Decodierphase ausgeführt werden kann. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 53 Kapitel 5. Rechnerarchitektur 4. Ausführungsphase. Zu Beginn der 80er ging man davon aus, dass nur solche Befehle verwendet werden sollten, die in einem Takt auszuführen waren. Aus Leistungsgründen sind aktuelle Prozessoren von diesem strengen RISC-Prinzip abgewichen. Die Rechenwerke moderner Prozessoren haben meist mehrer ALUs für bestimmte Anwendungen. Die verschiedenen ALUs dürfen, gesteuert durch das Leitwerk, mehrer Befehle gleichzeitig ausführen. Man spricht von superskalaren Rechenwerken. Das Leitwerk muss dafür sorgen, dass die Programmsemantik erhalten bleibt. 5. Rückschreibephase. Auch die Rückschreibephase wird durch das Load/Store-Prinzip beschleunigt. Es müssen lediglich die von der ALU kommenden Daten in die Register zurückgeschrieben werden. Bei Store-Befehlen kann es passieren, dass auf den Speicher gewartet werden muss, dies kann durch den Einsatz von Caches beschleunigt werden. Zusätzlichen können noch Schreibpapier verwendet werden. 6. Adressierungsphase. In der Adressierungsphase müssen die Befehlszähler für die in den verschiedenen Pipelinestufen steckenden Befehle hochgezählt oder bei Sprungbefehlen entsprechend angepasst werden. 5.2.6 Was bringt Pipelining? Pipelining bewirkt eine dramatische Performanceverbesserung. Rein theoretisch gilt: Zeit pro Befehl mit Pipelining = Zeit pro Befehl ohne Pipelining Anzahl der Pipelinestufen Dies würde gelten, wenn die Stufen perfekt balanciert währen, alle Befehl von einander unabhängig währen und es keine Sprünge gebe. 5.2.7 Wo wird Pipelining angewendet? In allen modernen Prozessoren. 5.2.8 Was ist RISC? siehe 5.2.2. 5.2.9 Warum arbeiten aktuelle Intel-Prozessoren intern wie RISC stellen aber nach aussen CISC-Befehle zur Verfügung? Für vernünftige Nutzung des Pipelining-Konzept eignen sich nur RISC-Befehle. Um die Kompatibilität zu gewährleisten werden die nach aussen durch CISC-Befehle versteckt. 5.2.10 Was ist MMX? MMX ist die Intel Multimedia Extension. Das sind zusätzliche Prozessorbefehle die der schnelle Verarbeitung von Multimediadaten dienen. Es handelt sich dabei um SIMD-Befehle, wobei ein Befehl einen ganzen Satz von Daten verändern kann. Für MMX wurden keine neuen Register eingeführt, es werden 64 Bit der 80 Bit FPU-Register verwendet. Daher keine gleichzeitigen FP- und Multimediaberechnungen. Für den MMX-Nachfolger ISSE wurden neue 128 Bit Register eingeführt. 5.2.11 Welche Prozessorarchitekturen kennen Sie? Vorsicht: Die folgenden Daten stammen aus vielen unterschiedlichen Quelle, die sich in einigen Punkten auch klar widersprechen, sind also mit grosser Vorsicht zu behandeln. 54 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.2. Zentralprozessoren Hersteller Architektur # Befehle # Pipelines # Pipelinestufen 1st level cache [kb] 2nd level cache [kb] # Register Registerlänge [Bit] max Taktfrequenz [MHz] # Transistoren max SPECint2000 max SPECfp2000 5.2.12 MIPS 10k Alpha 21164 Pentium II Itanium SGI RISC 183 4? 5? 32D / 32I ? ? ? 250 6,8M ? ? Compaq RISC ≈ 200 4 7 / 10 8D / 8I 96 ? 64 600 9,3M 928 (21264-C) 1482 (21364) Intel CISC ≈ 390 1 14 8D / 8I offchip 8 32 2000 7,5M 1130 (Pentium 4) 1103 (Pentium 4) Intel & HP EPIC (RISC/CISC) ? ? 10 16D / 16I 96 > 256 64 1000 25M (ohne Cache) 674 (Itanium 2) 1431 (Itanium 2) Wie viele Pipelinestufen haben moderne Prozessoren? Die Intel P6 Architektur hat 14 Stufen. 5.2.13 Welche Probleme können beim Pipelining auftreten? Die Probleme, die beim Pipelining auftreten können, nennt man Fliessbandhemmnisse, sie lassen sich in drei Gruppen aufteilen. Modifikation des Fliessbands durch explizite Ablaufbefehle. Sprungbefehle, Prozeduraufrufe, o. Ä. stellen Fliessbandhemmnisse dar, da von der sequentielle Befehlsfolge abgewichen wird. Das für die Befehlsholphase zuständige Teilwerk kann nicht einfach den nächsten Befehl holen, sondern muss an eine bestimmte Stelle springen. Dies kann bei bedingten Sprüngen auch noch von einer Berechnung abhängig sein. Die übliche Arbeitsweise des Teilwerks muss daher unterbunden werden. Es können keinen neuen Befehle mehr in die Pipeline nachgefüllt werden, bis der neue Befehlszähler zur Verfügung steht. Es gibt mehrere Techniken zur Optimierung. Sprungzielspeicher. Der Sprungzielspeicher ist ein Puffer der die zuletzt angesprungenen Sprungziele speichert. Die Idee ist, dass ein einmal angesprungenes Ziel sehr wahrscheinlich nochmal angesprungen wird, zum Beispiel bei Schleifen. Verzögerter Sprung. Bei dieser Technik wird der auf den Verzweigungsbefehl folgende Befehl vor der Verzweigung ausgeführt. Damit gibt man dem Leitwerk einen zusätzlichen Takt Zeit auf den Sprungzielspeicher oder andere Quelle zuzugreifen. Verzweigungsbefehle die so vorgehen heissen Delay Slot Befehle. Spekulative Ausführung. Bei der spekulative Ausführung werden die Befehle auf einem der beide Pfade der Verzweigung spekulativ ausgeführt. Die erzielten Ergebnisse werden dabei in Schattenregistern gehalten und Notfalls wieder gelöscht. Bedingte Ausführung. Die bedingte Ausführung kann bei Unterscheidungen, die sich nur in einem Maschinenbefehl niederschlagen, angewandt werden. Der Befehl wird nur ausgeführt, wenn die Bedingung war ist, sonst wird er übersprungen. Hierdurch realisiert man Fallunterscheidungen ohne Verzweigungen. Verzögerung in einzelnen Phasen des Fliessbands. Speicherzugriffe können durch Caches und zusätzliche Puffer beschleunigt werden. Datenabhängigkeiten. Fliessbandhemmnisse durch Datenabhängigkeiten können auftreten, wenn der Befehl i + 1 Werte benötigt, die der Befehl i Berechnet. Es kann nur vorkommen, dass der Befehl i seine Rückschreibephase noch nicht abgeschlossen hat und die Daten damit noch nicht verfügbar sind. Dieses Probleme kann durch Bypasses gelöst werden. Die Ergebnisse, die die ALU für den Befehl i berechnet hatte werden über Abkürzungen sofort zur Berechnung des nächsten Befehls verwendet. Diese Bypasses reduzieren die Abkürzungen erheblich führen aber zu sehr komplizierten Datenpfaden. Generell gilt bei allen Problemen, dass die Compiler sehr viel zur Optimierung beitragen können (oder müssen). So können sie z. B. durch geschickte Optimierung der Befehlsreihenfolge Datenabhängigkeit vermeiden. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 55 Kapitel 5. Rechnerarchitektur 5.2.14 Wie verhält sich eine Pipeline bei einer Unterbrechung? Wenn eine Unterbrechung auftritt, führt eine moderne Pipeline folgende Schritt durch. 1. Ein Trap-Befehl wird bei der nächsten Befehlsholphase in die Pipeline eingeführt. 2. Für den Befehl, der die Unterbrechung ausgelöst hat und für alle darauf folgenden Befehle, wird Schreibzugriff generell abgeschaltet. Dies gilt nicht für Befehle, die vor dem Unterbrechung auslösenden Befehl liegen. 3. Nach die dem Unterbrechungbehandlungsroutine abgelaufen ist, wird der Befehlszähler wieder auf den unterbrechende Befehl gesetzt. Wenn dieses Vorgehen bei einer Pipeline möglich ist, so spricht man von einer Pipeline mit Precise Interrupts. Für aufwendige Fliesspunktbefehle ist es schwer dieses Verhalten sicher zustellen. Deshalb haben moderne Prozessoren zwei Pipelines (oder zwei Betriebsmodi), die eine ist schnell, hat dafür keine Precise Interrupts, die andere ist langsamer und hat Precise Interrupts. Precise Interrupts sind in der Integerpipeline relativ einfach zu realisieren und werden dort auch realisiert, weil die Integerpipeline zur Implementierung von Virtual Memory nötig ist. 5.2.15 Warum verwendet man bei modernen Rechner mehrere Pipelines? siehe 5.2.16 und 5.2.14. 5.2.16 Warum geht es spezielle Pipelines für Festpunkt- und Fliesspunktarithmetik? Wichtig für das Pipelining-Konzept ist, dass die Befehle in etwa gleiche Zeit in den einzelnen Pipelinestufen verbringen. Da Fliesspunktoperation in der Regel deutlich mehr Zeit als Festpunktoperationen benötigen, ergeben sich Probleme bei der Mischung von beiden Befehlen. Eine Möglichkeit ist, sie auf verschiedene Pipelines aufzuteilen. Die Synchronisation zwischen mehreren Pipelines kostest natürlich. Ein anderer Grund sind die Unterbrechungen, siehe 5.2.14. 5.2.17 Wie lang dauert ein RISC-Befehl in etwa? Ursprünglich war es so gedacht, dass ein RISC-Befehl in der Ausführungsphase einen Takt benötigt. Angenommen er braucht in allen Phasen des Maschinenzyklus” einen Takt, so benötigt er bei streng sequentieller Ausführung 5 Takte. Wenn Pipelining perfekt wäre würde er bei einer 5-stufigen Pipeline im Durchschnitt 1 Takt benötigen. Mit geschätztem Overhead sagt man, dass eine Operation ca. 1,2 Takte benötigt. 5.2.18 Was sind Delay-Slot-Befehle? siehe 5.2.13. 5.2.19 Was ist ein Sprungziel-Keller? siehe 5.2.13. 5.2.20 Welche Adressierungsarten gibt es? Es gibt virtuelle und physische Adressierung, für die Umsetzung ist die MMU zuständig. 5.2.21 Kennen Sie Beispiele für unkonventionelle Rechner? Feldrechner: ein gewöhnlicher Programmspeicher, ein gewöhnliches Leitwerk, aber k Rechenwerke in regulärer Anordnung. Vektorrechner: ein gewöhnlicher Programmspeicher, ein gewöhnliches Leitwerk, ein m-stufiges Rechenwerk nach Pipelineprinzip, in dem m Komponenten von Vektoren in verschiedenen Phasen der gleichen Operation sind. assoziativer Rechner: ein gewöhnlicher Programmspeicher, ein gewöhnliches Leitwerk, alle Daten und Befehle in p Zellen eines assoziativen Speichers, d. h. Auswahl der Zellen nur durch inhaltliche Kriterien, nicht durch Adressen 56 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.3. Hauptspeicher und Verkehrswege 5.3 Hauptspeicher und Verkehrswege 5.3.1 Page Faults werden woran erkannt und von wem behandelt? Seitenfehler werden von der MMU erkannt, diese löst bei einem Seitenfehler eine Unterbrechung aus, die von einer Unterbrechungsroutine des Betriebssystems behandelt wird. Seiten-Ein- und Auslagerungsstrategien stecken in dieser Routine. 5.3.2 5.3.3 Speicherhierarchie mit Zugriffszeiten aufzählen. Speicher Zugriffszeit (Prozessortakt 2.5 ns) Kapazität Register Primär-Cache Sekundär-Cache Hauptspeicher Plattenspeicher Archivspeicher < 2.5 ns 2.5 - 10 ns 5 - 50 ns 50 - 500 ns 5 - 15 ms > 50 ms 256 - 1024 Bytes 1 - 128 KB 265 KB - 4MB ≤ 4 GB ≤ 200 GB mehrere TB Was ist Caching? Ein Cache ist ein schneller Pufferspeicher zwischen den Registern und dem Hauptspeicher. Darin sind sowohl Programmteile wie Daten enthalten. Diese können entweder zusammen in einem Unified Cache liegen oder es gibt zwei getrennte Caches (Split Cache). Bei jedem Speicherzugriff des Prozessors testet die Cache Steuerlogik des Caches, ob das Speicherwort im Cache vorliegt (Cache Hit) oder nicht (Cache Miss). Falls es vorliegt wird es direkt vom Cache in den Prozessor übertragen. Falls ein Cache Miss auftritt, wird aus dem Hauptspeicher ein Block von Worten in den Cache übertragen. Hauptspeicher und Cache sind in Blöcken organisiert. Wegen der hohen Geschwindigkeit wird der Cache durch Hardware verwaltet, der Zugriff erfolgt transparent. Programmierer, Übersetzer und Betriebssysteme sollten den Cache aber insofern beachten, dass sie Daten und Programm für das Caching optimieren. So wird eine Programmschleife sicherlich deutlich schneller abgearbeitet, wenn sie komplett in den Cache passt. Moderne Systeme haben meistens eine Cache-Hierarchie bestehend aus dem Primary Cache und dem Secondary Cache die sich in Grössen und Geschwindigkeit unterscheiden. Primary Cache. Der Primary Caches ist auf dem Prozessor-Chip integriert und meist als Split Cache realisiert. Die Cache Blöcke sind meist nicht länger als 32 Bytes und damit in wenigen Zyklen nachladbar. Secondary Cache. Bis vor kurzem wurde der Secondary Cache ausserhalb des Prozessors realisiert, Prozessoren wie der Pentium III haben ihn aber bereits On Die. Der Secondary Cache ist meist ein Unfied Cache mit Blockgrössen um die 128 Byte. 5.3.4 Was für Adressierungsmöglichkeiten gibt es bei Caches? Caches können entweder mit realen oder virtuellen Adressen adressiert werden, beides hat Vor- und Nachteile. Virtuelle Adressierung. Der Vorteil virtueller Caches ist, dass sie schneller sind, da Cache-Zugriff und Adressübersetzung durch die MMU können parallel stattfinden. Im Mehrprozessbetrieb werden virtuelle Caches aber einige Probleme auf. Dieselbe virtuelle Adressen von verschiedenen Prozessen kann auf unterschiedliche physische Adresse verweisen, deshalb muss beim Kontextwechsel der Cache gelöscht werden. Unterschiedliche virtuelle Adressen können auf dieselbe physische Adresse verweisen, z. B. bei gemeinsamen Variablen. Um Inkonsistenzen im Cache zu vermeiden sollten gemeinsame Daten nicht gecachet werden. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 57 Kapitel 5. Rechnerarchitektur Wenn Hauptspeicherdaten durch DMA Zugriffe verändert werden, liegt eine Dateninkonsistenz vor. Das Problem bei virtueller Adressierung ist das Rückermitteln der virtuellen Adressen aus den physischen Adressen. Virtuelle Caches werden deshalb hauptsächlich bei prozessorinternen Befehls-Caches eingesetzt. Reale Adressierung. Caches mit physischer Adressierung sind langsamer, da die Adressübersetzung abgewartet werden muss. Dafür sind sie für Inkonsistenzen nicht so anfällig. Zum Beispiel kann die DMA-Einheit, die real adressiert, hier für die Konsistenz sorgen. 5.3.5 Was für Cache-Organisationsformen gibt es? Die Organisationsform des Caches legt das Mapping von Hauptspeicher-Blöcken auf Cache-Blöcke und wie ein gewünschter Block im Cache aufzufinden ist (Identifikationsproblem) fest. Die folgenden Organisationsformen sind gebräuchlich. Direkte Abbildung. In einem Cache mit direkter Abbildung und N Blöcken, wird der Hauptspeicherblock B im Cache-Block B mod N gespeichert. Diese Abbildung ist die einfachste, da nur ein Vergleicher benötigt wird. Das Problem ist, dass Cache-Einträge sehr schnell wieder verdrängt werden. Voll assoziative Abbildung. In einem voll assoziativen Cache kann ein Hauptspeicherblock in jedem Cache-Block gespeichert werden. Voll assoziative Caches sind teuer, da man N Vergleicher benötigt. Die Verdrängung ist hier zwar am geringsten, aber sie sind langsam. n-fach assoziative Abbildung. Bei n-fach assoziative Caches wird der Cache in M = N/n Sätze mit jeweils n Blöcken geteilt. Der Hauptspeicher Block B kann im einem Cache-Block des Satz B mod M gespeichert werden. Wenn n = N ist, wird der Cache voll assoziativ, wenn n = 1 entspricht er einem Cache mit direkter Abbildung. n-fach assoziative Caches bieten die Möglichkeit eine guten Kompromiss zwischen Kosten, Geschwindigkeit und Wirksamkeit zu finden. In der Praxis werden 2,4 und 8-fach assoziative Caches eingesetzt. Da jedem Cache-Block eine grosse Anzahl an Hauptspeicherblöcken zugeordnet ist, braucht man einen Mechanismus um Hauptspeicherblöcke eindeutig zu identifizieren. Dazu wird pro Cache-Block neben ein paar Verwaltungsbits auch noch eine Kennung gespeichert, die der Hautpspeicheradresse entspricht. 5.3.6 Welche Aktualisierungsstrategien gibt es bei Caches? Wenn Daten im Cache verändert werden, stellt sich die Frage, wann diese Änderungen in den Speicher zurückgeschrieben werden um die Konsistenz zu erhalten. Es gibt zwei Strategien Durchschreiben (Write Through). Durchschreiben bedeutet, dass jede Änderung im Cache sofort auch in den Hauptspeicher geschrieben wird. Dadurch wird die Datenkonsistenz gewährleistet, aber der Prozessor-Speicherbus stark belastet. Primary Caches arbeiten meist nach diesem Prinzip. Zurückschreiben (Write Back ). Bei Write Back wird der Hauptspeicher erst aktualisiert, wenn ein veränderter Cache-Block verdrängt wird. Dazu muss ein Dirty-Bit verwaltet werden. Secondary Caches arbeiten meist so. 5.3.7 Welche Ersetzungsstrategien werden bei Caches eingesetzt? Bei Caches wird meist eine LRU-Strategie eingesetzt, wobei aber in den Cache-Blöcken eine Altersinformation gespeichert werden muss. Eine sehr einfach zu implementierende Strategie ist das zufällige auswählen der zu ersetzenden Seite. Diese Strategie liefert überraschend gute Ergebnisse. 5.3.8 Was fällt Ihnen zum Thema Leistung von Caches ein? Leistungsbetrachtung von Caches orientieren sich meist an der Formel für die mittlere Speicherzugriffszeit Te für eine zweistufige Speicherhierarchie. Te = h · T h + m · T m 58 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.4. Ein-/Ausgabewerk Wobei h und m = 1 − h die Treffer- bzw. Fehlerraten (Hit rate, miss rate) im Cache sind. T h ist die Cache-Zugriffszeit (Hit time) und Tm die Cache-Ladezeit (miss time). Die Zugriffszeit Te kann durch die folgende drei Massnahmen verbessert werden. Optimieren einer Grösse bewirkt meist die Verschlechterung einer anderen Grössen, es gilt einen Kompromiss zu finden. Reduktion der Fehlerrate m. Entweder kann der Cache vergrössert oder die Assoziativität erhöht werden. Dadurch wird er teurer und die Cache-Zugriffszeit wird erhöht. Durch grössere Blöcke können Lokalitätseigenschaften besser genutzt werden, dafür wird die Ladezeit grösser. Weitere Möglichkeiten sind der Einsatz eines Victim Cache und Prefetching. Reduktion der Cache-Zugriffszeit Th . Die Cache-Zugriffszeiten für den Primary Cache sind entscheidend für die Prozessortaktrate, Zugriffszeiten auf den Secondary Cache bestimmen die Wartezyklen des Prozessors. Kurze Zugriffszeiten erhält man bei kleinen und einfach organisierten Caches, was den obigen Optimierungen zuwiderläuft. Reduktion der Cache-Ladezeit Tm . Cache-Ladezeiten können durch Einführung einer weiteren Ebene in der Speicherhierarchie verbessert werden. Eine andere Möglichkeit ist die Verwendung von Non-Blocking Caches die Daten an den Prozessor liefern können während sie aus dem Hauptspeicher Daten nachladen. Ausserdem ist Tm massgeblich durch die Geschwindigkeit des Prozessor-SpeicherBusses und die Hauptspeicherzugriffszeiten bestimmt. 5.3.9 Was beeinflusst den Zugriff auf den Hauptspeicher? Hauptspeicherzugriffszeiten Geschwindigkeiten des Prozessor-Speicher-Busses. Anzahl und Effizienz der Caches 5.3.10 Wie kann man den Zugriff auf den Hauptspeicher beschleunigen? Durch Caching, siehe 5.3.3 5.3.11 Wie verläuft die Kommunikation Prozessor-Hauptspeicher? Über den Prozessor-Speicher-Bus. Zusätzlich werden meist ein oder mehrere Caches dazwischen geschaltet. 5.3.12 Wie funktioniert Busverwaltung? Ein Busmaster muss das Recht erwerben auf dem Bus zu senden. Wenn es nur einen Busmaster gibt, ist die Vergabe einfach. Bei mehreren Mastern muss irgendwie geregelt werden, wer das Recht erhält. Es gibt Verfahren mit einer zentralen Steuereinheit und mit dezentralen Einheiten in den Busmastern. Diese können prioritätengesteuert oder fair sein. 5.3.13 Was ist Daisy-Chaining? Beim Daisy-Chaining wird der Bus sozusagen durch die Geräte hindurchgeführt. Die Daten gehen in ein Geräte hinein, werden dort eventuell verarbeitet, und dann wieder raus zum nächsten Gerät. 5.4 Ein-/Ausgabewerk 5.4.1 Welche Varianten gibt es für E/A-Operationen? Die Gerätekontrolle haben Register über die sie gesteuert und über die Daten ausgelesen werden können. Es gibt zwei Möglichkeiten, wie diese Register angesprochen werden können: I/O-Ports. Hier wird den Registern der Controller eine I/O-Port-Nummer zugeordnet, zusätzlich gibt es spezielle IN/OUT Befehle. So kann z. B. mit IN RX, port der entsprechende Wert in ein CPU-Register geladen werden. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 59 Kapitel 5. Rechnerarchitektur Memory-Mapped I/O. Bei Memory-Mapped-IO werden die Register der Controller in Hauptspeicher eingeblendet. Nun kann auf die Register mit ganz normalen Move-Befehlen zugegriffen werden. Der Vorteil ist, dass man keine speziellen I/O-Befehle benötigt. Ausserdem kann der Zugriff auf Geräte durch die normalen Speicherschutzmechanismen realisiert werden. Auf Maschinenebene können Befehle eingespart werden, da der Wert nicht zwangsweise in ein Register geladen werden muss. Probleme ergeben sich durch das Caching, cachen von gemapten Adressen macht keinen Sinn, daher muss die Hardware dafür sorgen, dass das Caching für diese Adressen ausgeschaltet wird. Bei Memory-Mapped I/O müssen alle Gerätekontroller bei jedem Speicherzugriff aufpassen, ob nicht sie gemeint sind. Das ist kein Problem, solange I/O und Speicher am selben Bus hängen. Gibt es aber einen dedizierten Prozessor-Speicher-Bus, so muss man irgendwie zwischen echten Speicherzugriffen und Zugriffen auf Geräte unterscheiden. Dafür gibt es wiederum verschieden Ansätze. Bei Pentium gehen deshalb Speicherzugriffe durch die PCI-Bridge, die die Unterscheidung macht. Es gibt auch Systeme, z. B. Pentium, die beide Verfahren nutzen. 5.4.2 Was ist DMA? DMA steht für Direct Memory Access und wird benutzt um die CPU bei I/O-Zugriffen zu entlasten. Wenn DM eingesetzt wird, gibt es einen DMA-Controller. Will die CPU Daten von einem Geräte lesen, gibt sie dem DMA-Controller den entsprechenden Auftrag und wendet sich wieder ihrer Arbeit zu. Der DMA-Controller kopiert jetzt eigenständig Daten von dem Gerät in den Hauptspeicher. Dabei können Geräte nicht unterscheiden ob der Zugriff von der CPU oder dem DMA-Controller kommt. Wenn er damit fertig ist, schickt er eine Unterbrechung an die CPU. Damit kann die CPU vom einem Grossteil der I/O-Arbeit entlastet werden. 60 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 5.5. SPEC-Werte 5.5 SPEC-Werte Die folgenden Werten stammen von der SPEC-Website (www.spec.org) vom 16.03.2003. Die ersten 5 zeigen bei beiden Benchmarks die Top-5 von diesem Tag, die anderen sind beliebig ausgewählt. CINT2000 Company System Peak Base Processor Published Dell Precision WorkStation 350 (3.06 GHz P4) Intel D850EMVR motherboard (3.06 GHz, Pentium 4 processor with HT Technology) Precision WorkStation 340 (3.06 GHz P4) Precision WorkStation 350 (2.8 GHz P4) Intel D850EMVR motherboard (2.8 GHz, Pentium 4 processor) 1130 1085 Dec-2002 1107 1099 1074 1032 1061 1017 1040 1032 Intel Pentium 4 (533 MHz system bus) Intel Pentium 4 Processor with HT Technology (3.06 GHz, 533 MHz bus) Intel Pentium 4 (533 MHz system bus) Intel Pentium 4 (533 MHz system bus) Pentium 4 processor (2.8 GHz, 533 MHz bus) Precision WorkStation 340 (2.4 GHz P4) Intel D850MD motherboard (2.4 GHz, Pentium 4 processor) Precision WorkStation 340 (2.2 GHz P4) Precision WorkStation 340 (2.0A GHz P4) Intel D850EMV2 motherboard (2.0A GHz, Pentium 4 processor) Gigabyte GA-7DX Motherboard, AMD Athlon (TM) XP 1800+ PRIMEPOWER650 (810MHz) 905 865 Sep-2002 833 819 833 798 759 738 Intel Pentium 4 (533 MHz system bus) Pentium 4 processor (2.4 GHz, 400 MHz bus) Intel Pentium 4 (400 MHz system bus) Intel Pentium 4 759 756 644 Intel Dell Dell Intel Dell Intel Dell Dell Intel AMD Fujitsu Siemens Dell Compaq HP PowerEdge 1500SC (1.26 GHz PIII) AlphaServer GS160 Model 16 68/1001 hp workstation j6700 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs Dec-2002 Dec-2002 Dec-2002 Oct-2002 Apr-2002 Sep-2002 Feb-2002 621 Pentium 4 processor (2.0A GHz, 400 MHz bus) AMD Athlon (TM) XP 1800+ Sep-2002 Oct-2001 624 512 SPARC64 GP Jul-2002 623 621 611 561 Intel Pentium III Alpha 21264C Feb-2002 Jul-2001 603 569 PA-8700 May-2001 61 Kapitel 5. Rechnerarchitektur CFP2000 Company System Peak Base Processor Published HP HP AlphaServer GS1280 7/1150 hp server rx5670 (1000 MHz, Itanium 2) hp server rx2600 (1000 MHz, Itanium 2) hp workstation zx6000 (1000 MHz, Itanium 2) hp AlphaServer ES45 68/1250 1482 1431 1124 1431 Alpha 21364 Intel Itanium 2 Jan-2003 Dec-2002 1427 1427 Intel Itanium 2 Dec-2002 1422 1422 Intel Itanium 2 Dec-2002 1365 1019 Alpha 21264C Nov-2002 1060 1048 Dec-2002 1048 1034 1036 1024 1024 1012 844 843 733 782 Intel Pentium 4 (533 MHz system bus) Pentium 4 processor (2.8 GHz, 533 MHz bus) Intel Pentium 4 (533 MHz system bus) Pentium 4 processor (2.67 GHz, 533 MHz bus) UltraSPARC III Cu AMD Athlon (TM) XP 2800+ Mar-2003 Oct-2002 831 772 AMD Athlon (TM) XP 2700+ Oct-2002 628 618 Aug-2001 607 591 Pentium 4 processor (1.8 GHz, 400 MHz bus) Intel Pentium 4 May-2001 604 561 AMD Athlon (TM) XP 1700+ Oct-2001 HP HP HP Dell Intel Dell Intel Sun AMD AMD Intel Dell AMD 62 Precision WorkStation 350 (2.8 GHz P4) Intel D850EMVR motherboard (2.8 GHz, Pentium 4 processor) Precision WorkStation 350 (2.66 GHz P4) Intel D850EMVR motherboard (2.67 GHz, Pentium 4 processor) Sun Fire V1280 (900MHz) ASUS A7N8X (REV 1.02) Motherboard, AMD Athlon (TM) XP 2800+ ASUS A7N8X (REV 1.02) Motherboard, AMD Athlon (TM) XP 2700+ Intel D850GB motherboard(1.8 GHz, Pentium 4 processor) Precision WorkStation 330 (1.70 GHz P4) Epox 8KHA+ Motherboard, AMD Athlon (TM) XP 1700+ Oct-2002 Dec-2002 Oct-2002 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 Kapitel 6 Betriebssysteme 6.1 Allgemeines 6.1.1 Was für Arten von Betriebsmitteln gibt es? Betriebsmittel können wie folgt klassifiziert werden. einmalig benutzbar. Druckerpapier, Nachricht wiederholt benutzbar. parallel benutzbar. Datei mit lesendem Zugriff exklusiv benutzbar, unterbrechbar. CPU exklusiv benutzbar, nicht-unterbrechbar. Drucker 6.1.2 Was ist ein Betriebssystem? Ein Stück Software, das die in 6.1.3 beschriebenen Aufgaben erfüllt. 6.1.3 Was sind die Aufgaben eine Betriebssystems? Veredeln der Hardware (Virtualisierung) Steuerung und Kontrolle der Programmausführung Verwaltung der Betriebsmittel Anbieten von Diensten in Form von Schnittstellen, so dass die Betriebsmittel genutzt werden können. Schutz mehrerer gleichzeitig aktiver Benutzer 6.2 Prozesse und Threads 6.2.1 Was ist ein Prozess? Ein Prozess ist eine Abstraktion eines laufenden Programmes. Mit einem Prozess verbunden sind: Thread of Control Adressraum globale Variablen offene Dateien Kindprozesse Register Befehlszähler DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 63 Kapitel 6. Betriebssysteme Stack Status 6.2.2 Was ist ein Thread? Ein Thread könnte als leichtgewichtiger Prozess bezeichnet werden. Er hat Thread of Control Register Befehlszähler Stack Status Er hat aber keinen eigenen Adressraum globale Variablen offene Dateien Kindprozesse 6.2.3 Was bedeutet Mehrprogrammbetrieb? Wenn mehrere Prozesse sich eine CPU teilen spricht man von Mehrprogrammbetrieb. 6.2.4 Wie kommunizieren Prozesse miteinander? Prozesse können kommunizieren über: gemeinsamen Speicher gemeinsame Dateien Nachrichten Semaphoren RPC Pipes Mailslots (Windows) 6.2.5 Was beinhaltet der Prozesskontext? Prozessmanagement Speichermanagement Dateimanagement Register Zeiger auf Codesegment Root-Verzeichnis Befehlszähler Zeiger auf Datensegment Arbeitsverzeichnis Prozess-Status-Wort Zeiger auf Stacksegment File Handles Stackpointer User ID Prozessstatus Gruppen ID Priorität PID 6.2.6 64 Welche Zeitscheibenverfahren gibt es? http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.2. Prozesse und Threads Round-Robin Round-Robin mit Prioritäten 6.2.7 Welche Möglichkeiten gibt es zur Prozesssynchronisation? Unterbrechungssperre Verdrängungssperre Sperrvariablen algorithmische Lösungen Semaphore Monitore 6.2.8 Was ist ein Scheduler? Der Scheduler ist der Teil des Betriebssystems, der entscheidet, wann welcher Prozess Rechenzeit bekommt. Er folgt einem Scheduling Algorithmus. 6.2.9 Was ist ein Dispatcher? In Multithreadinganwendung gibt es oft einen Dispatchthread. Dieser hat die Aufgabe die Arbeit an andere Threads zu verteilen. 6.2.10 Erzählen Sie etwas zu Scheduling-Strategien. Man muss unterscheiden zwischen Scheduling-Strategien für Batch-Systeme. Für Batch-Systeme gibt es folgende Algorithmen First-Come First-Served. Sehr einfach umzusetzen, wegen der Nicht-Unterbrechbarkeit aber nicht praktikabel. Shortest Job First. Beweisbar kürzeste Verweilzeit (siehe 6.2.11), aber nicht unterbrechbar und die Job-Grösse muss im voraus bekannt sein. Shortest Remaining Time Next. Hier wählt der Scheduler immer den Prozess mit der kleinsten Restlaufzeit. Auch hier müssen Berechnungszeiten im voraus bekannt sein. Interaktive Systeme. Round-Robin. Jedem Prozess wird ein Zeitintervall, das Quantum, zugeordnet, in dem er laufen kann, danach wird zu einem neuen Prozess gewechselt. Die Prozesse können in einer Liste verwaltet werden. Wenn ein Prozess sein Quantum aufgebraucht hat (oder abgegeben hat) wird an das an der Liste angehängt und der Prozess am Kopf der Liste gestartet. Die Wahl der Länge des Quantums ist entscheidend. Ist das Quantum zu kurz, gibt es zu viele (teure) Kontextwechsel, ist es zu lang, sind die Antwortzeiten schlecht. In der Praxis werden Quanta der Länge 20–50 ms eingesetzt. Prioritäten. Beim Prioritäten-Scheduling wird jedem Prozess eine Priorität zugeordnet. Der Scheduler wählt immer den Prozess mit der höchsten Priorität. Damit dieser nicht für immer Läuft, bricht man nach einem bestimmten Zeitquantum ab und nimmt den nächsten. Oder man verringert die Priorität des Prozesses entsprechend seiner Laufzeit. Falls seine Priorität unter dies eines anderen fällt, wird gewechselt. Generell können die Prioritäten statisch oder dynamisch vergeben werden. Bei der dynamischen Vergabe gibt es folgende Varianten. Viele Prozesse verbringen sehr viel Zeit mit I/O und belasten die CPU nur wenig. Solchen Prozessen sollte man die CPU so prompt wie möglich zur Verfügung stellen (wenn sie sie benötigen) damit sie sich weiter mit I/O befassen können und die CPU für rechenintensive Prozesse frei bleibt. Eine einfache Möglichkeit, diesen Service anzubieten, ist, den Prozessen die Priorität 1/f zuzuweisen. Wobei f der Bruchteil des Quantums ist, den der Prozess beim letzten mal benutzt hat. Prozesse die ihr Quantum fast gar nicht nutzen, bekommen eine hohe Priorität, Prozesse die ihr Quantum voll nutzen eine niedrige Priorität. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 65 Kapitel 6. Betriebssysteme Round-Robin + Prioritäten. In der Praxis wird oft eine Kombination der beiden Schedulingstrategien eingesetzt. Die Prozesse werden in mehrere Kategorien (nach Prioritäten) eingeteilt und in innerhalb dieser Kategorien wird Round-Robin Scheduling gemacht. Das heisst der Scheduler macht solange Round-Robin in der wichtigsten Kategorien bis dort keine Prozesse mehr vorhanden sind, dann nimmt er sich die nächste Kategorie vor. Auch hier sollten die Prioritäten dynamisch angepasst werden, sonst besteht die Gefahr des Verhungerns. Shortest Process Next. Shortest Process Next ist der Versuch die optimalen Verweilzeiten von Shortest Job First in interaktive Systemen zu nutzen. Dazu geht man davon aus, dass interaktive Prozesse immer nach dem Muster Warten auf Befehl, Befehl ausführen, Warten auf Befehl, Befehl ausführen ablaufen. Wenn man die Ausführung eines Befehls als Job betrachtet, kann man die Antwortzeiten minimieren in dem man die kürzesten Befehl zuerst bearbeitet. Um herauszufinden, welcher der kürzeste Befehl ist, wird eine Technik namens Aging eingesetzt. Garantiertes Scheduling. Bei diesem Verfahren werden den Benutzer Garantien wie bei N Benutzern bekommt jeder genau 1/N der Rechenleistung gegeben und diese Versucht einzuhalten. Dies ist schwierig zu implementieren. Lottery. Hier bekommt jeder Prozessor ein Los . Wenn eine Schedulingentscheidung getroffen werden muss, wird eine Losnummer ausgelost. Der Prozess mit der gezogenen Nummer erhält den Prozessor. Um Prioritäten zu realisieren kann bestimmten Prozessen mehr als ein Los gegeben werden und damit ihre Gewinnchancen erhöht werden. Lottery Scheduling bietet einige interessante Möglichkeiten, so können z. B. Prozesse ihre Lose an andere Prozesse verschenken. Ein Client, der unbedingt eine Information von einem Server braucht, könnte seine Lose an den Server verschenken, damit dieser möglichst schnell Rechenzeit bekommt. Fair-Share. Beim Fair-Share Scheduling bezieht der Scheduler zusätzlich Informationen über den Besitzer eines Prozesses in Betracht und achtet darauf, dass alle Benutzer ungefähr gleich viel Rechenzeit erhalten. Echtzeit-Systeme. Schedulingalgorithmen für Real-Time Systeme können entweder statisch oder dynamisch sein. Statische Algorithmen weisen jedem Prozess eine feste Priorität und verwenden dann Prioritätenscheduling. Bei dynamischen Algorithmen können sich die Prioritäten ändern. Rate Monotonic. RMS ist ein statischer Algorithmus der jedem Prozess eine Priorität entsprechend der Frequenz des Ereignis, das diesen Prozess triggert, zuweist. Dazu müssen die Ereignisse periodisch auftreten und die Abarbeitung eines Ereignisses muss immer gleich lang dauern. Earliest Deadline First. Ein typische dynamischer Algorithmus ist EDF. Wenn ein Prozess die CPU benötigt meldet er sich an und gibt den Termin zu dem er fertig sein muss an. Die Prozesse werden in einer Liste verwaltet und der Scheduler wählt immer den Prozess mit der knappsten Deadline. Eine weitere Möglichkeit bietet die Trennung von Mechanism und Policy. Wenn Programme aus mehreren Prozessen bestehen haben sie meisten eine sehr viel bessere Vorstellung davon wie diese geschedult werden sollen als der Scheduler des Betriebssystems. Ein Möglichkeit diese Information zu nutzen ist Betriebsystemfunktionen zur Verfügung zu stellen die es einem Prozess erlauben, die Prioritäten seiner Kindprozesse zu verändern. Hier steckt der Mechanism im Kernel aber die Policy wird durch einen Userprozess bestimmt. 6.2.11 Welche Strategie führt zu kürzesten Verweilzeiten? Shortest Job First. Angenommen es gibt 4 Prozesse A, B, C, D, wobei deren Bearbeitungszeiten a, b, c, d sind. Wenn die Prozesse in alphabetischer Reihenfolge ausgeführt werden, dann sind jeweiligen Verweilzeiten für die Prozesse 66 T (A) = a T (B) T (C) = a+b = a+b+c T (D) = a+b+c+d http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.2. Prozesse und Threads Das ergibt eine durchschnittliche Verweilzeit von T (∅) = = T (A) + T (B) + T (C) + T (D) 4 4a + 3b + 2c + d 4 Wie man sieht, hat die Laufzeit des ersten Prozesses den grössten Einfluss, daher führt SJF zu den kürzesten Verweilzeiten. 6.2.12 Ist Shortest Job First unterbrechbar? Nein. Ist gibt aber eine unterbrechbare Version, die sich Shortest Remaining Time Next nennt. Hier wählt der Scheduler immer den Prozess mit der kleinsten Restlaufzeit. Auch hier müssen Berechnungszeiten im voraus bekannt sein. 6.2.13 Welche unterbrechbare Strategie liefert die kürzesten Verweilzeiten? Shortest Remaining Time Next. 6.2.14 Arbeitet das Prioritätenscheduling mit statischen Prioritäten? siehe 6.2.10. 6.2.15 Wie funktionieren dynamische Prioritäten? siehe 6.2.10. 6.2.16 Wonach bewertet man Schedulingstrategien? Je nach Art des Systems verfolgen die Strategien unterschiedliche Ziele. Für alle System gilt: Fairness. Policy Enforcement. Der Scheduler muss die System-Policy durchsetzen. Balance. Alle Teile des Systems sollten ausgelastet sein. Für interaktive Systeme gilt: Response Time. Proportionality. Das Scheduling sollten den Erwartungen des Users entsprechen. Zum Beispiel macht es dem User in den meisten Fällen nichts aus, wenn das Versenden einer eMail 20s dauert, es stört in aber wenn das Eingeben eines Buchstabens 20s dauert. Für Batch-Systeme gilt: Throughput. Anzahl der Jobs pro Stunde maximieren. Turnaround Time. Verweilzeiten minimieren. CPU Utilization. CPU-Auslastung maximieren. 6.2.17 Wie schützt das Betriebssystem den Speicherbereich eines Prozesses vor Zugriffen von anderen Prozesses? Das muss die Hardware erledigen, die muss vom Betriebssystem richtig konfiguriert werden. 6.2.18 Wie läuft eine Unterbrechung genau ab? 1. Hardware speichert Befehlszähler, PSW usw. auf dem aktuellen Stack DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 67 Kapitel 6. Betriebssysteme 2. Hardware setzt Befehlszähler entsprechend des Interrupt Vectors 3. Assembler-Routine sichert die Register 4. Assembler-Routine legt neuen Stack an 5. C-Routine mit Unterbrechungscode wird gestartet 6. Scheduler wählt neuen Prozess 7. Rücksprung von der C-Routine zur Assembler-Routine 8. Zurückkopieren der Register, usw., Starten des (neuen) aktuellen Prozesses 6.2.19 Was für Arten von Unterbrechungen gibt es? Interrupts lassen sich nach vielen Kriterien klassifizieren, z. B. Software vs. Hardware, Maskable vs Non-Maskable, Precise vs Imprecise. 6.2.20 Was beinhaltet der Prozesskontrollblock? Der Prozesskontrollblock ist eine Datenstruktur, die den Prozesskontext beschreibt. Siehe 6.2.5. 6.2.21 Was ist der Unterschied zwischen Ports und Sockets? Ein Socket ist ein Kommunikationsendpunkt. Sockets sind für Kommunikation zwischen zwei Rechnern gedacht. Ein Socket setzt sich zusammen aus Adresse und Port. 6.2.22 Wie realisiert man Semaphoren in der Praxis? Falls ein TSL-Befehl zur Verfügung steht können Semaphoren wie in 6.2.23 realisiert werden. Ansonsten werden sie meist vom Betriebssystem durch kurzzeitiges Sperren der Interrupts realisiert. Das Betriebssystem stellt den Userprozessen dann entsprechende Funktionen zur Verfügung. 6.2.23 Wie wird wechselseitiger Ausschluss umgesetzt? Es gibt verschiedene Möglichkeiten wechselseitigen Ausschluss umzusetzen. Interrupts sperren. Ein Prozess könnte, bevor er eine kritischen Bereich betritt einfach alle Interrupts sperren. Das hat mehrere Nachteile. Ersten sollte in Userprozess nicht das Recht haben Interrupts zu sperren, zweitens funktioniert dies in Mehrprozessorsystemen nicht. Innerhalb des Betriebssystems kann das Sperren von Interrupt manchmal eingesetzt werden, für Userprozesse sollte es aber nicht in Frage kommen. Test and Set Locks. TSL ist eine Befehl, der von der Hardware angeboten wird. Er liest ein Wort aus dem Speicher und speichert es in einem Register. Anschliessend schreibt z. B. 1 an die Stelle im Speicher. Die Hardware garantiert, dass dieser Befehl ununterbrechbar ist. Der folgende Code zeigt wie mit diesem Befehl wechselseitiger Ausschluss realisiert werden kann: enter: TSL REG, lock CMP REG, 0 JNE enter RET kopiere lock in das Register REG und setze lock auf 1 war lock = 0 ? es war nicht 0, also war die Sperre bereits gesetzt ⇒ Schleife zurück zum Aufrufer, kritischer Bereich wurde betreten MOV lock, 0 RET 0 in lock speichern und dadurch Sperre frei geben zurück zum Aufrufer, kritischer Bereich wurde verlassen exit: Natürlich müssen die beiden Prozeduren beim Betreten bzw. Verlassen des kritischen Bereichs aufgerufen werden. Das Problem ist, dass diese Implementierung Busy-Waiting benutzt. 68 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.3. Deadlocks Mutexes. Mutexes sind nicht anderes als binäre Semaphoren. Sie sind ideal um wechselseitigen Ausschluss zu realisieren. Meist werden sie als Betriebssystemfunktionen implementiert, z. B. durch Sperren von Interrupts. Falls ein TSL-Befehl zur Verfügung steht können sie aber auch sehr einfach im Userspace implementiert werden. mutex lock: TSL REG, mutex CMP REG, 0 JZE ok CALL thread yield JMP mutex lock ok: RET mutex unlock: MOV mutex, 0 RET 6.3 Deadlocks 6.3.1 Was sind Deadlocks? kopiere mutex in das Register REG und setze mutex auf 1 war mutex = 0 ? es war Null, also alles klar es war nicht 0, also war die Sperre bereits gesetzt ⇒ schlafen gehen Thread ist wieder aufgewacht, nochmal probieren zurück zum Aufrufer, kritischer Bereich wurde betreten 0 in mutex speichern und dadurch Sperre frei geben zurück zum Aufrufer, kritischer Bereich wurde verlassen Eine Menge von Prozessen ist in einem Deadlock wenn jeder Prozess auf ein Ereignis wartet, das nur von einem anderen Prozess ausgelöst werden kann. 6.3.2 Welche Verfahren gibt es mit Deadlocks umzugehen? 1. Vogel-Strauss-Algorithmus. Mann hofft darauf, dass keine Deadlocks auftreten. Dies ist das in der Praxis am häufigsten verwendete Verfahren. 2. Deadlock-Erkennung. Man versucht zu erkennen, dass ein Deadlock aufgetreten ist und die Situation dann zu bereinigen. 3. Deadlock-Vermeidung. Man versucht Deadlocks durch vorsichtige Vergabe der Betriebsmittel zu vermeiden. 4. Deadlock-Verhinderung. Man versucht Deadlocks generell zu verhindern, in dem man eine der vier Deadlock-Bedingung ausschliesst. 6.3.3 Wie funktioniert Deadlock-Erkennung? Um einen Deadlock zu finden muss man einen Kreis im Betriebsmittelgraphen finden. Falls ein Deadlock gefunden wurden, gibt es verschiedene Methoden damit umzugehen. Recovery through Preemption. Wenn möglich entzieht man einem der Prozesse ein Betriebsmittel. Selbst wenn dies theoretisch möglich ist, ist es schwer automatisch festzustellen welchem Prozess welches Betriebsmittel zu entziehen ist. Recovery through Rollback. Man kann Prozesse zwingen hin und wieder sog. checkpoints zu speichern. In diesen wird der komplette Status eines Prozesses festgehalten. Wenn nun ein Deadlock entdeckt wird, rollt man einen der Prozessen der eines beteiligten Betriebsmittel anfordert zu seinem letzten Checkpoint zurück. Recovery through Killing. Die einfachste Methode ist es einen der am Deadlock beteiligten Prozesse zu killen. Wenn der Deadlock dadurch nicht behoben wird, killt man weitere Prozesse. Alternativ kann man auch nicht am Deadlock beteiligte Prozesse killen. In der Praxis geht das natürlich schlecht, da dadurch Daten verloren gehen. Ein Beispiel wo es funktioniert sind Transaktionen in Datenbanken. 6.3.4 Wie funktioniert Deadlock-Vermeidung? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 69 Kapitel 6. Betriebssysteme Bei der Deadlock-Vermeidung versucht man zu verhindern, dass ein Prozess in einen unsicheren Zustand kommt. In der Praxis ist dies selten anzuwenden, da man dafür die zukünftigen Ressourcenbedarf der Prozesse kennen muss. Ein Beispiel ist der Bankiersalgorithmus. 6.3.5 Wie funktioniert Deadlock-Verhinderung? Man versucht eine der vier Deadlock-Bedingung generell auszuschliessen. 1. Mutual exclusion condition. Mann kann wie im Falle des Druckers einen Spooler einrichten. Dadurch hat nur noch ein einziger Prozess Zugriff auf ein Betriebsmittel. Das Problem ist, dass man nicht alle Betriebsmittel spoolen kann, z. B. die Prozesstabelle. Ausserdem können sich dadurch neue potentielle Deadlocks ergeben. Der Printspooler braucht Plattenspeicher, auch dieser kann deadlocken. 2. Hold and wait condition. Man könnte Prozesse zwingen alle ihre Betriebsmittel auf einen Schlag anzufordern, dies geht aber meist nicht, da die Prozesse zu Beginn noch nicht wissen, welche Betriebsmittel sie benötigen. In bestimmten Fällen funktioniert kann das sehr gut funktionieren. Datenbanken setzen Two-Phase Locking ein, was im Prinzip diesem Ansatz entspricht. 3. No preemption condition. Das geht mit vielen Betriebsmittel einfach nicht anders. 4. Circular wait condition. Eine Möglichkeit ist den Betriebsmittel eindeutige Nummern zuzuordnen und Prozessen das Anfordern von Betriebsmittel nur in numerischer Reihenfolge zu erlauben. Dadurch können keine Zyklen im Betriebsmittelgraphen und damit auch keine Deadlocks entstehen. Das Problem ist, dass es in einem echten System fast unmöglich ist, eine vernünftige Nummerierung aller Betriebsmittel zu finden. 6.3.6 Was sind die Bedingungen für einen Deadlock? Damit ein Deadlock eintreten kann, müssen die folgende vier Bedingungen gelten. 1. Mutual exclusion condition. Jedes Betriebsmittel ist entweder genau einem Prozess zugeordnet oder frei. 2. Hold and wait condition. Ein Prozess der bereits Betriebsmittel besitzt kann weitere anfordern. 3. No preemption condition. Betriebsmittel können Prozessen nicht weggenommen werden. Die Prozesse müssen die Betriebsmittel freiwillig abgeben. 4. Circular wait condition. Es gibt eine kreisförmige Kette mit zwei oder mehr Prozessen, die jeweils auf ein Betriebsmittel warten, das vom nächsten Nachbarn der Kette belegt wird. 6.3.7 Was sind sichere Zustände? Eine Zustand ist sicher, wenn er nicht in einem Deadlock ist und es eine Scheduling Reihenfolge gibt, so dass alle Prozesse abgearbeitet werden können, selbst dann wenn alle Prozesse plötzlich die Maximale Anzahl an Betriebsmittel fordern. 6.3.8 Was ist der Bankiersalgorithmus? Der Bankiersalgorithmus verwendet das Prinzip der Verklemmungsvermeidung. Bei einer Anfrage eines Kunden wird der Betrag probeweise zugeteilt und dann überprüft der Algorithmus, ob in der entstehenden Situation einen Verklemmung möglich ist, wenn ja, verweigert er die Geldausgabe. Dafür ist aber nötig, dass der Algorithmus die zukünftigen Anforderungen der Prozesse (Kunden) abschätzen kann (Kreditrahmen). 6.3.9 Was ist ein Betriebsmittelgraph? Mit einem Betriebsmittelgraph lässt sich graphisch darstellen welche Prozesse welche Betriebsmittel belegen und welche Prozesse welchen Betriebsmittel anfordern. 70 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.4. Speichermanagement 6.4 Speichermanagement 6.4.1 Was ist ein virtueller Speicher? Virtueller Speicher ist Speicher der Programmen voll zur Verfügung steht, der aber grösser als der tatsächlich vorhandene, physische Speicher ist. 6.4.2 Welche Verfahren zur Seitenersetzung kennen Sie? Optimaler Algorithmus. Der optimale Algorithmus ist ein in der Praxis nicht umsetzbarer Algorithmus der aber zur Qualitätsbeurteilung von echten Algorithmen benutzt wird. Algorithmus. Wenn eine Page Fault auftritt liegt eine bestimmte Anzahl von Seiten im Speicher. Jeder dieser Seiten kann (theoretisch) eine Zahl zugeordnet werden, die der Anzahl der Instruktionen, die vergehen, bis diese Seite benutzt wird, entspricht. Der optimale Algorithmus entfernt die Seite mit der höchsten Zahl, deren Benutzung also am weitesten in der Zukunft liegt. Vor- und Nachteile. Es liegt auf der Hand, dass dieses Verhalten optimal aber nicht realisierbar ist. NRU. Der Not Recently Used Algorithmus teilt Seiten entsprechende ihrer Reference- und ModifiedBits in Klassen ein und wählt eine möglichst lange nicht benutzte Seite. Algorithmus. In den meisten Systemen gibt es Reference- und dein Modified-Bit. Falls dein Page Fault auftritt, teilt der Algorithmus die Seiten in vier Gruppen ein Class Class Class Class 0: 1: 2: 3: not referenced, not modified not referenced, modified referenced, not modified referenced, modified Nun entfernt der Algorithmus zufällig eine Seite aus der Klasse mit der niedrigsten Zahl. Vor- und Nachteile. Es werden keine Seiten, die stark benutzt werden, entfernt und die Implementierung ist einfach. Die Leistung des Algorithmus ist nicht wirklich optimal. FIFO. Einfaches FIFO-Prinzip. Algorithmus. Die Seiten werden in einer Queue verwaltet, falls ein Page Fault auftritt, wird die älteste Seite entfernt und die Neue an das Ende der Queue angehängt. Vor- und Nachteile. Die Implementierung ist sehr einfach aber das Problem ist, dass die älteste Seite nicht unbedingt auch eine Seite ist, die selten genutzt wird. Second Chance. Second Chance ist eine Erweiterung von FIFO die das Reference-Bit mit in die Entscheidung einbezieht. Algorithmus. Falls ein Page Fault auftritt geht der Algorithmus erst mal so vor wie FIFO und betrachtet den Kopf der Liste. Falls die das Reference-Bit der Seite am Kopf der Queue nicht gesetzt wird, wird sie entfernt. Falls es gesetzt ist, bekommt sie eine zweite Chance und wird an das Ende der Queue gehängt und der Algorithmus schaut sich die zweit-älteste Seite an. Vor- und Nachteile. Ein Problem von Second Chance ist, dass es, wenn bei allen Seiten das Reference-Bit gesetzt ist, zu FIFO degenerieren kann. Das andere Problem ist, dass das ständige Umhängen von Seiten in der Queue aufwendig ist. Clock. Clock ist nur eine bessere Implementierung von Second Chance. Algorithmus. Bei Clock sind die Seiten in einer kreisförmigen Liste wie bei einer Uhr angeordnet. Ein Zeiger zeigt auf die älteste Seite. Wenn nun ein Page Fault auftritt, prüft der Algorithmus ob das Reference-Bit der ältesten Seite gesetzt ist. Wenn nicht, wird die Seite ersetzt. Falls es gesetzt ist, wird es gelöscht und der Zeiger eine Position weiter gestellt. Vor- und Nachteile. Dieser Algorithmus behebt die Implementierungsprobleme von Second Chance und stellt einen realistischen Algorithmus dar. LRU. Der Least Recently Used Algorithmus entfernt immer die Seite, die am längsten nicht benutzt wurde. Algorithmus. Man nimmt an, dass Seiten in letzter Zeit stark benutzt wurden, wohl auch in Zukunft noch benutzt werden, deshalb möchte man die am längsten nicht benutzte Seite entfernen. Grundsätzlich könnte der Algorithmus mit einer Liste, die immer so organisiert wird, dass die am längsten nicht genutzte Seite am Ende steht, realisiert werden. Dies ist aber sehr aufwendig. Mit ein bisschen zusätzlicher Hardware geht es deutlich einfacher. Dazu benötigt man einen Counter DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 71 Kapitel 6. Betriebssysteme der nach jeder Instruktion erhöht wird, ausserdem muss der Seitentabelleneintrag ein Feld haben um einen Counterwert aufzunehmen. Bei jedem Speicherzugriff wird der aktuelle Counterwert in dem entsprechenden Seitentabelleneintrag gespeichert. Falls ein Page Fault auftritt, wird die Seite mit dem niedrigsten Counterwert entfernt. Eine andere Hardwareimplementierung verwendet eine Matrix zur Speicherung derselben Information. Vor- und Nachteile. Sehr guter Algorithmus, der aber Unterstützung der Hardware erfordert. NFU. Not Frequently Used ist ein einfacher Versuch LRU mit Software zu simulieren. Algorithmus. NFU verwendet einen Software-Counter für jede Seite der beim Einlagern der Seite auf 0 gesetzt wird. Bei jedem Clocktick durchläuft das Betriebssystem alle Seiten und addiert den Wert des Reference-Bits auf den Counter. Falls ein Page Fault auftritt, wird die Seite mit dem niedrigsten Counterwert entfernt. Vor- und Nachteile. Das Hauptproblem ist, dass NFU niemals etwas vergisst. Seiten die vergangener Zeit extrem häufig benutzt wurden, haben einen hohen Counterwert und verlieren diesen nicht mehr, auch wenn sie später gar nicht mehr benutzt werden. Aging. Aging ist eine kleine Modifikation von NFU die den Algorithmus sehr nah an LRU bringt. Algorithmus. Wieder hat jede Seite einen eigenen Counter. Diesmal wird der Counterwert bevor das Reference-Bit addiert wird um ein Bit nach rechts verschoben. Ausserdem wird der Wert nicht auf das Bit am weitesten rechts, sondern auf das Bit am weitesten links, addiert. Wenn ein Page Fault auftritt wird die Seite mit dem kleinsten Counterwert entfernt. Eine Seite die lange nicht benutzt wurde, hat viele führende Nullen und damit einen kleinen Wert. Vor- und Nachteile. Dieser Algorithmus nähert LRU recht gut, verhält sich in zwei Punkten aber doch unterschiedlich. Da pro Intervall von Clocktick zu Clocktick nur ein Bit gespeichert wird, kann man nicht zwischen unterschiedlichen Zeitpunkten in einem Intervall speichern. Der andere Unterschied ist auf die endliche Anzahl von Bits in den Countern zurückzuführen. Falls zwei Seiten beide den Counter-Wert 0 haben, hat man keine Möglichkeit zu beurteilen, welche älter ist. In der Praxis sind 8 Bits aber meist ausreichend da damit bei einem Clocktick-Intervall von 20ms ein Zeitraum von 160ms überwacht werden kann. Working Set. Alle bisher beschriebenen Algorithmen verwenden die Strategie des Demand Paging. Ein Prozess wird zuerst ohne seine Seiten gestartet, die benötigten Seiten werden dann Stück für Stück on demand eingelagert. Reale Programme haben meist bestimmte Lokalitätseigenschaften. D. h. sie greifen während einer bestimmten Phase meist nur auf wenige ihrer Seite zu. Die Menge der Seiten, die ein Prozess gerade benutzt, nennt man das Working Set. Bei den bisher aufgeführten Ersetzungsstrategien, treten sehr viele Page Faults auf, bis das Working Set eines Prozesses im Speicher ist. Beim Mehrprogrammbetrieb werden Prozesse oft komplett aus dem Speicher entfernt. Die Idee ist nun dem Prozess, bevor er wieder gestartet wird, sein Working Set zur Verfügung zu stellen. Diese Information muss vor dem Verdrängen des Prozesses gespeichert werden. Das Einlagern der Seiten bevor der Prozess anläuft, wird als Prepaging bezeichnet. Algorithmus. Wenn die Information über das Working Set eines Prozesses bekannt ist, kann man daraus auch einen effizienten Seitenersetzungsalgorithmus ableiten. Falls ein Page Fault auftritt, sollte eine Seite entfernt werde, die nicht Teil des Working Sets ist. Das Merken des Working Sets ist sehr aufwendig, daher gibt es verschiedene Näherungslösungen. Eine besteht darin sich zu merken welche Seiten der Prozess in den letzten τ Sekunden benutzt hat. Die CPU-Zeit die ein Prozess seit seinem Start tatsächlich benutzt hat, nennt man current virtual time. Der Ersetzungsalgorithmus arbeitet wie folgt. Wie immer ist in den Seitentabelleneinträgen das Reference-Bit gespeichert. Zusätzlich hat jeder Eintrag noch ein Feld in dem die Zeit gespeichert wird, wann die Seite zuletzt benutzt wurde. Bei jedem Clocktick wird das Reference-Bit gelöscht. Falls nun ein Page Fault auftritt, wird die ganze Seitentabelle durchlaufen und wie folgt vorgegangen. Ist das Reference-Bit gesetzt, wird die aktuelle Zeit im Eintrag gespeichert, dadurch wird gekennzeichnet, dass die Seite benutzt wurde, als der Page Fault auftrat. Da diese Seite benutzt wurde, gehört sie zum Working Set und sollte nicht entfernt werden. Falls das Reference-Bit 0 ist, wurde die Seite nicht benutzt und stellt eine Kandidaten für die Auslagerung dar. Jetzt wird zuerst ihr Alter berechnet, (current virtual time minus Zeit der letzten Benutzung). Falls das Alter grösser als τ ist, gehört die Seite nicht mehr zum Working Set und wird ersetzt. Falls ihr Alter kleiner als τ ist, gehört die Seite zum Working Set und wird verschont. Man merkt sich aber die Seite mit dem grössten Alter. Falls sich beim Durchlauf rausstellt, dass alle Seiten zum mit gelöschten Reference-Bit zum Working Set gehören, wird die mit dem höchsten Alter entfernt. Falls bei allen Seiten das Reference-Bit gesetzt 72 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.4. Speichermanagement ist, wird eine Seit ausgelost (am besten eine saubere). Vor- und Nachteile. Der Algorithmus ist sehr gut, aber aufwendig zu implementieren. Ausserdem muss immer die komplette Seitentabelle durchlaufen werden. WSClock. Dieser Algorithmus ist eine vereinfachte Implementierung des Working Set Algorithmus der die Idee des Clock Algorithmus’ aufgreift. Algorithmus. Wieder wird pro Seitentabelleneintrag das Reference-Bit, das Modified-Bit und die Zeit des letzten Zugriffs gespeichert. Diesmal werden die Einträge in eine kreisförmigen Liste gespeichert und es gibt einen Zeiger der auf einen der Einträge zeigt. Zuerst wird wieder das Reference-Bit getestet. Ist es gesetzt wurde diese Seite kürzlich benutzt und sollte nicht entfernt werden. Der Algorithmus löscht das Reference-Bit und geht zur nächsten Seite. Wenn er dort eine Seite, deren Reference-Bit nicht gesetzt ist, vorfindet, geht er wie folgt vor. Ist das Alter dieser Seite grösser als τ gehört sie nicht zum Working Set. Falls sie dazu auch noch sauber ist, wird sie einfach ersetzt. Falls sie nicht sauber ist, kann sie nicht einfach ersetzt werden. Um einen Prozesswechsel zu vermeiden, wird sie nicht direkt auf die Platte geschrieben, sondern der Schreib-Auftrag wird geschedult und der Zeiger auf die nächste Seite gesetzt. Es könnte ja sein, dass sich noch eine alte, saubere Seite findet. Was passiert nun, wenn der Algorithmus einmal alle Seiten durchlaufen hat ohne eine alte, saubere Seite zu finden. Jetzt müssen zwei Fälle unterschieden werden 1. mindestens ein Schreib-Auftrag wurde geschedult 2. kein Schreib-Auftrag wurde geschedult Im ersten Fall macht der Algorithmus einfach weiter, früher oder später findet er eine alte, saubere Seite, da der Schreib-Auftrag irgendwann ausgeführt wird. Im zweiten Fall befinden sich alle Seiten im Working Set, sonst währe ein Schreib-Auftrag abgegeben worden. Jetzt sucht sich der Algorithmus einfach eine saubere Seite und ersetzt sie (die Position einer sauberen Seite kann er sich während des Durchlaufs merken). Falls es keine saubere Seite gibt, dann hilft alles nichts, die aktuelle Seite wird geschrieben und ausgetauscht. Vor- und Nachteile. Wegen der recht einfachen Implementierung und der guten Performance, wird WSClock in der Praxis häufig eingesetzt. 6.4.3 Was ist Fragmentierung? Man unterscheidet zwischen interner und externer Fragmentierung. Interne Fragmentierung. Im Zusammenhang mit Paging spricht man von interner Fragmentierung. Damit ist die Speichervergeudung gemeint, wenn Seiten nur halb belegt werden. Die interne Fragmentierung spricht für eine kleine Seitengrösse, es gibt aber auch Argumente für grosse Seitengrössen. Externe Fragmentierung. Im Zusammenhang mit Segmentierung spricht man von externer Fragmentierung. Hiermit ist das über die Dauer entstehende Muster aus belegtem Speicher und Löcher gemeint, das beim Ein- und Auslagern von Segmenten entsteht. Mit Compaction kann man die Löcher wieder zu einem einzigen grossen Loch zusammenfassen, was aber sehr aufwendig ist. 6.4.4 Was ist Paging? Von Paging spricht man, wenn Prozess nicht unbedingt komplett, sondern auch teilweise im Speicher gehalten werden können. 6.4.5 Was ist Swapping? Von Swapping spricht man, wenn jeder Prozess immer komplett in den Speicher geladen wird. Beim Prozesswechsel wird der Prozess (und alle zugehörigen) Daten auf die Platte ausgelagert und der neue Prozess eingelagert. 6.4.6 Welche Programmierschnittstelle bietet die Speicherverwaltung an? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 73 Kapitel 6. Betriebssysteme Die meisten Betriebssysteme stellen den Userprozessen Systemaufrufe zum Anfordern und Freigeben von Speicher zur Verfügung. Bei Unix heisst der Befehl zur Veränderung der Grösse des Datensegmentes brk. Zusätzlich gibt es meist noch Befehle um Memory-Mapped Files einzurichten. Die Verwaltung des virtuelle Speichers sollte für Prozesse und Programmierer transparent sein. Ein Ausnahme ist der Einsatz von Shared Memory. Wollen sich zwei Prozesse zum schnellen Austausch von Daten Speicher teilen, muss die Programmierschnittstelle der Speicherverwaltung Funktionen anbieten, damit Prozesse solche gemeinsamen Speicherbereiche einrichten können, z. B. Memory-Mapped Files. Windows bieten auch einige Funktionen an um das Paging zu kontrollieren, so kann man z. B. für eine bestimmten Speicherbereich das Paging ganz ausschalten. 6.4.7 Wie erkennt man, dass auf eine Seite zugegriffen wurde? Im Seitentabelleneintrag gibt es ein Reference-Bit. Diese wird gesetzt sobald auf die Seite zugegriffen wird. 6.4.8 Ist die MMU ein Teil des Betriebssystems? Nein. Sie ist heute meist Teil der CPU, kann bei älteren Systemen aber auch ausserhalb der CPU sein. 6.4.9 Welche Speicher-Einlagerungsstrategien kennen Sie? On-Demand. Alle Seitenersetzungsstrategien ausser den auf Working Sets basierenden, verwenden diese Strategie. Seiten werden dann eingelagert, wenn sie das erste Mal gebraucht werden. Prepaging. Wenn die Working Set Information bekannt ist, kann das Working Set eines Prozesses eingelagert werden, bevor er gestartet wird. 6.4.10 Was ist ein Working Set? Die Menge der Seiten, die ein Prozess gerade benutzt, nennt man das Working Set. 6.4.11 Was ist das Buddy Verfahren? Das Buddy-Verfahren wird von Linux zur Speicherverwaltung eingesetzt. Falls ein Stück Speicher benötigt wird, wird die Grösse des Stücks zuerst auf eine Potenz von 2 aufgerundet und dann im Speicher vorhandene Löcher solange halbiert bis es genau passt. Werden zwei nebeneinander liegende Speicherbblöcke (Buddies) frei, werden sie wieder zusammengefasst. 6.5 I/O 6.5.1 Welche Schichten hat die E/A-Verwaltung? User-level I/O Software Device-independent OS software Device drivers Interrupt handler Hardware 6.6 Betriebssystem-Design 6.6.1 Was ist ein Microkernel? 74 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.6. Betriebssystem-Design Die Idee eines Microkernels ist es nur minimale Funktionalität in den Kernel einzubauen um die Effizienz und Zuverlässigkeit zu steigern. Zum Beispiel werden das Dateisystem und das Speichermanagement in Userprozesse ausgelagert. Durch die Modulare Struktur sind Microkernel-Systeme deutlich übersichtlicher und daher besser wartbar. Die viele Funktionen in Userprozesse ausgelagert sind, sind sie stabiler. Das abstürzen eine Userprozesses reisst den Kernel normalerweise nicht mit. Durch die mit der Modularisierung verbundenen häufigen Wechsel zwischen User- und Kernelprozessen ist die Performance von Microkernelsystemen etwas schwächer. Mach und Minix sind Microkernelsysteme. 6.6.2 Was bedeutet monolithisch? Eine monolithische Struktur ist das genau Gegenteil eines Microkernels. Bei monolithischen Systemen steckt alle betriebssystemrelevante Funktionalität im Kernel. 6.6.3 Welche Funktionen beinhaltet ein Microkernel? Scheduling 6.6.4 Was gehört zum einem Betriebssystemkern? Das kommt auf die Struktur des Systems an, Microkernel oder monolithisch. Was immer drin ist, ist der Scheduler. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 75 Kapitel 6. Betriebssysteme 6.7 Beispiele 6.7.1 Erklären Sie doch mal die Unterschiede zwischen Unix und Windows 2000. Unix/Linux Windows Grösse (LOC) Struktur Linux 2.2: 1 M, Solaris 5.8: 2 M Die Struktur hängt von der jeweiligen Unix-Version ab. So hat MINIX z. B. eine Microkernel, Linux dagegen ist monolithisch. Schnittstelle Unter Unix rufen Prozessen direkt die System Calls auf. Diese werden über Bibliotheken zur Verfügung gestellt. Prozesse können bei Linux generell nur durch forken eines Prozesses erzeugt werden. Dadurch entsteht eine Prozesshierarchie. Die Unixprozesse entsprechen fast genau den Prozessbegriff wie er z. B. im Tanenbaum verwendet wird. Ein Prozess hat einen Thread of Control und ihm sind Ressourcen aller Art zugeordnet. 29 M Ab Version 4.0 ist NT absolut monolithisch, vorher war es zwar auch kein Microkernel, aber es waren doch einige Betriebssystemfunktionen in Userprozesse ausgelagert. Windows versteckt die eigentlichen System Calls hinter einer schwergewichtigen API mit enormen Funktionsumfang. Eine Prozesshierarchie gibt es Windows nicht. Windows führt einige Begriffe ein. Ein Job ist eine Sammlung von Prozessen, zu jedem Prozess gehören einer oder mehr Threads. Prozesse stellen eher eine Verwaltungseinheit dar, ihnen fehlt eigentlich der Thread of Control. Auf Userebene kann ein Thread mehrere Fibers, leichtgewichtige Threads enthalten. Auch Windows verwendet Round-Robin Scheduling mit dynamischen Prioritäten wobei Prozesse die Prioritäten ihrer Threads beeinflussen können. Das Windows Scheduling basiert auch nur auf Threads. Fibers können nur von Prozessen selbst geschedult werden. Jedem Prozess stehen 4GB Speicher zur Verfügung wobei in die oberen zwei GB bei jedem Prozesse das Betriebssystem eingeblendet ist, das aber geschützt ist. Das liegt daran, dass bei Windows bei einem Wechsel vom User- zum Kernelmodus der selbe Prozess (Thread) weiter läuft. Ein Pagingdaemon versucht kontinuierlich den Speicher möglichst frei zu halten. Der verwendete Ersetzungsalgorithmus basiert auf einem Working Set. Windows verwendet aber kein Prepaging. Windows bietet diverse Möglichkeiten zur Kommunikation, darunter Pipes, Named Pipes, Mailslots, Sockets, RPC und Shared Files. Prozesse Scheduling Unix und Linux verwenden Round-Robin Scheduling mit dynamischen Prioritäten. Das Linux Scheduling basiert auf Threads. Speicher Das Paging wird bei Unix von einem PagingDaemon durchgeführt der eine modifizierte Variante des Clock-Algorithmus’ einsetzt. Linux verwendet den Buddy-Algorithmus und ebenfalls einen Clock-ähnlichen Ersetzungsalgorithmus. Es gibt keine Working Set Verwaltung und kein Prepaging. IPC Prozesskommunikation läuft bei Unix vorwiegend über Pipes, die eine Form des Nachrichtenaustausches darstellen. Zusätzlich können Prozesse über Signale kommunizieren (siehe 6.7.3). Unix ordnet jedem Benutzter eine UID dar und organisiert die Benutzter in Gruppen. Dateien werden die bekannten Rechte entsprechend der Userund Gruppen-ID zugeordnet. Durch das Everything is a File-Konzept lässt sich dieses Prinzip z. B. auch auf Ein- und Ausgabe anwenden. Unix folgt dem Everything is a File-Konzept. Sicherheit Misc 76 Windows wird jeder Benutzter und jede Gruppe durch eine eindeutige Security ID (SID) identifiziert. Jeden Objekt kann eine ACL (siehe 3.5.4) zugeordnet werden, die dem User entsprechende Operation auf einem Objekt gestattet. Bei Windows ist alles ein Objekt. http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 6.7. Beispiele 6.7.2 Hat Windows 2000 einen Microkernel? In den ersten Versionen hatte NT zwar einen rechten schlanken Kernel, da er aber Speichermanagement, Dateisystem u. Ä enthielt, konnte man nicht von einem Microkernel sprechen. Seit der Version 4.0 steckt ein Grossteil der Funktionalität im Kernel, also erst recht kein Microkernel. 6.7.3 Was für Möglichkeiten bietet Unix zur Prozesskommunikation? Pipes. Pipes stellen eine Form von Kommunikation über Nachrichten da. Zwischen zwei Prozessen kann ein Kanal angelegt werden. Jeder der Prozesse kann einen Datenstrom in diesen Kanal schicken. Diese Kanäle heissen Pipes. Software Interrupts. Prozesse können Signale an andere Prozesse verschicken. Die Prozesse können dem System mitteilen wie es mit eingehenden Signalen umgehen soll. Dazu muss ein Prozess ein Signalbehandlungsroutine angeben. Falls ein Signal eingeht, wird diese Routine sofort gestartet. Signale können nur an Prozesse aus der eigenen Prozessgruppe (die Verwandten) geschickt werden. Signal werden auch für andere Zwecke benutzt, so bekommt ein Prozess der durch 0 teilt ein SIGFPE Signal geschickt. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 77 Kapitel 7 Rechnernetze 7.1 Allgemeines 7.1.1 Wozu verwendet man Codierungen? Codierung dient dazu, den Inhalt einer Nachricht in einer speicherbaren, verarbeitbaren und übermittelbaren Form darzustellen. 7.1.2 Welche Fehler treten in Netzwerken auf? Daten können kaputt gehen Daten können verloren gehen Daten können in der falschen Reihenfolge ankommen Leitungen können ganz zusammenbrechen 7.1.3 Wie kann man Fehler erkennen? Das hängt von der Art der Fehler ab. Störungen innerhalb eines Frames können durch Prüfsummen erkannt werden. Verloren gegangene Frames können durch Bestätigungen erkannt werden. Flusskontrolle stellt die richtige Reihenfolge sicher. 7.1.4 Unterschiede zwischen verbindungsorientierter und verbindungsloser Kommunikation? Bei verbindungsorientierter Kommunikation muss der Sender zum Empfänger eine (virtuelle) Verbindung aufbauen, erst dann kann der Daten senden. Danach wird die Verbindung wieder abgebaut. Eine Verbindung wird als eine sichere Kommunikation betrachtet. Normalerweise geht man davon aus, dass die Daten bei Empfänger in der selben Reihenfolge ankommen, wie sie vom Sender verschickt wurden. Dies ist bei verbindungsloser Kommunikation nicht sicher gestellt. Bei verbindungsloser Kommunikation sendet der Sender einfach darauf los. Die Daten kommen beim Empfänger an oder auch nicht. 7.1.5 Was ist ein Multicast? Bei einem Multicast kann eine Station an eine Gruppe von Empfänger eine Nachricht verschicken. Dies muss unterschieden werden vom Broadcast, wo eine Station an alle anderen Stationen eine Nachricht schickt. 7.1.6 Was sind LAN, MAN und WAN? Name Distanzen Beispiel Technologien LAN MAN WAN Local Area Network < 1km Büro Ethernet, WLAN Metropolitan Area Network 1 km – 10 km Stadt Cable, FDDI Wide Area Network > 10 km Land ATM DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 79 Kapitel 7. Rechnernetze 7.2 ISO–OSI–Referenzmodell 7.2.1 i Gegen Sie eine Kurzeinführung in das ISO–OSI–Schichtenmodell. 1. Physical Layer (Physische Schicht). Die physische Schicht ist dafür zuständig rohe Bits über den Kommunikationskanal zu schicken. Hier geht es um mechanische und elektrische Fragen, ausserdem spielt das Timing eine wichtige Rolle. Die Implementierung ist meist stark von dem darunter liegenden Leiter abhängig. Dienste. SendFrame und RecieveFrame Protokolle. Stichworte. Twisted Pair, Glasfaser, Funk, Satellit, Manchester-Codierung 2. Data Link Layer (Sicherungsschicht). Die Sicherungsschicht berücksichtigt, dass die von der physischen Schicht gelieferten Bitströme mit Fehler behaftet sein können. Sie ist für Fehlerkontrolle und Korrektur zuständig. Ausserdem teilt die Sicherungsschicht die zu übertragenden Daten in sog. Frames auf. Desweiteren findet hier die Flusssteuerung statt. Bei Netzen mit Vielfachzugriffsmedien gibt es noch eine Teilschicht, die MAC-Layer, die für die Medienzugriffskontrolle sorgt. Dienste. SendPacket, RecievePacket Protokolle. HDLC, SLIP, PPP Stichworte. Sliding Window, Bit-Stuffing 3. Network Layer (Netzwerkschicht). Die Netzwerkschicht muss Pakete von der Quelle zum Ziel versenden können. Der Weg verläuft normalerweise über mehrere Router, deshalb findet das Routing auf der Netzwerkschicht statt. Die Netzwerkschicht stellt somit die erste Ende-zu-Ende Übertragungschicht dar, die Sicherungsschicht überträgt die Daten nur von einem Ende des Leiters zum anderen. Ausserdem werden Stauprobleme in Netzwerken in der Netzwerkschicht gelöst (und in der Transportschicht). Dienste. SendTPDU, RecieveTPDU Protokolle. IP, ARP, DHCP Stichworte. Staukontrolle, Routing, IP 4. Transport Layer (Transportschicht). Die Aufgabe der Transportschicht ist es einen zuverlässigen, kostengünstigen Transport von der Quelle vom Ziel anzubieten. Dieser Transport muss unabhängig von der physischen Struktur der Netzwerke die dazu dienen, sein. Manchmal werden die Schichten in zwei Teile zerlegt, die unteren vier Schichten betrachtet man als den Transport Service Provider und die oberen Schichten als den Transport Service User. Die Transportschicht stellt somit die Schnittstelle zwischen Anbieter und Nutzer da, deshalb spielt sei eine besondere Rolle. Dienste. Connect, SendData, RecieveData, Disconnect Protokolle. TCP, UDP Stichworte. Sliding Window, Three-Way Handshake 5. Session Layer (Sitzungsschicht). Die Sitzungsschicht erlaubt es Benutzern an verschiedenen Maschinen Sitzungen zwischen ihnen aufzubauen. Eine Sitzung ermöglicht gewöhnlichen Datentransport, wie die Transportschicht auch, bietet aber zusätzliche Dienste, die für bestimmte Anwendungen wichtig sind. Die Sitzungsschicht existiert im TCP/IP-Modell nicht und ist logisch schlecht von der Anwendungsschicht trennbar. 6. Presentation Layer (Darstellungsschicht). Die Darstellungsschicht führt bestimmte Funktionen aus, deren häufige Verwendung eine allgemeine Lösung rechtfertigen. Die Darstellungsschicht kümmert sich auch um Syntax und Semantik der übertragenen Information. Die Darstellungsschicht existiert im TCP/IP-Modell nicht und ist logisch schlecht von der Anwendungsschicht trennbar. Protokolle. MIME 7. Application Layer (Anwendungsschicht). In der Anwendungsschicht sind viele bekannte Protokolle wie HTTP angesiedelt. Protokolle. HTTP, FTP, SNMP, SMTP 80 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.2. ISO–OSI–Referenzmodell Host A 7 Application Application Presentation protocol 6 Presentation 5 Host B Application protocol Presentation PPDU Session protocol Session APDU Session SPDU Transport TPDU Transport protocol 4 Transport Router Router 3 Network Network Network Network Paket 2 Data link Data link Data link Data link Frame 1 Physical Physical Physical Physical Medium Medium Bit Medium Die Darstellung zeigt das ISO-OSI-Schichtenmodell. Wichtig sind die folgenden Begriffe Dienst (Service). Ein Dienst ist eine Menge von Operationen die eine Schicht der Schicht über ihr anbietet. Es wird nichts über die Implementierung dieser Operationen ausgesagt. Dienstprimitive. Die Dienstprimitive sind die Operation (Methoden), die eine Schicht der Schicht über ihr anbietet. Es herrscht einige Verwirrung darüber welche Schicht nun welche Primitive hat. Zum Beispiel wird manchmal behauptet, die Schicht 1 hätte die Primitiven SendBit und RecieveBit. Das stimmt so nicht. Das sind nämlich genau die Primitiven des Leiters und nicht der Schicht 1. Protokoll. Protokolle definieren Regel für die Nachrichten die zwischen Instanzen der selben Schicht ausgetauscht werden. Zwei Instanzen auf der selben Schicht können, wenn Sie wollen sich für ein neues Protokoll entscheiden. Solange sie ihre Dienste nicht ändern, wird das Netz davon nicht beeinflusst. 7.2.2 Was sind die Anforderungen an die einzelnen Schichten? siehe 7.2.1. 7.2.3 Nennen Sie für jede Schicht ein Beispielprotokoll. siehe 7.2.1. 7.2.4 Wer hat das OSI-Schichtenmodell entworfen? Das OSI-Modell basiert auf einem Vorschlag der von der ISO ausgearbeitet wurde. 7.2.5 Warum vertikale und nicht horizontale Einteilung? Mit vertikaler Einteilung wird ein Schichtenmodell impliziert, siehe 7.2.8. Horizontale Einteilung entspricht einer Partitionierung mit komplizierteren Abhängigkeiten. 7.2.6 Was bedeutet das Open ins OSI? Unter Open Systems versteht man Systeme die für die Kommunikation mit anderen Systemen offen sind. 7.2.7 Unterschied zwischen ISO–OSI–Referenzmodell und Schichtenmodell des Internet? Das TCP/IP-Modell spezifiziert weder Schicht 1 noch Schicht 2. Ausserdem nimmt es oberhalb der Schicht 4 keine Trennung mehr vor. Die Darstellung zeigt die beiden Modelle. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 81 Kapitel 7. Rechnernetze OSI 7 Application 6 Presentation 5 Session 4 Transport 3 Network 2 Data link Physical 1 7.2.8 TCP/IP Application Transport Network Host-tonetwork Was für einen Sinn hat das Schichtenmodell? Reduzierung der Komplexität und Austauschbarkeit. 7.2.9 Was ist ein SAP? Ein SAP ist ein Service Access Point oder ein Dienstzugriffspunkt. Dies ist der Punkt in dem die Schicht n der Schicht n + 1 ihre Dienste zur Verfügung stellt. 7.3 Schichtübergreifend. 7.3.1 Auf welcher Schicht ist ein Hub angesiedelt? Physische Schicht. 7.3.2 Auf welcher Schicht ist ein Switch angesiedelt? Sicherungsschicht. 7.3.3 Auf welcher Schicht ist eine Bridge angesiedelt? Sicherungsschicht. 7.3.4 Auf welcher Schicht ist ein Router angesiedelt? Netzwerkschicht. 7.3.5 Beschreiben Sie Ethernet. Ethernet ist eine LAN-Technologie, die durch IEEE 802.3 standardisiert ist und eine Übertragungsrate von 10 MBit/s bietet. Ethernet kann mit Coax, Twisted-Pair und Glasfaser eingesetzt werden. Es handelt sich um ein Broadcastnetz, dass CSMA/CD als Zugriffsverfahren einsetzt und die ManchesterCodierung verwendet. Als Adressen verwendet Ethernet die weltweit eindeutigen 48 Bit MAC Adressen. Ethernet selbst bietet nur verbindungslose Kommunikation ohne Bestätigung. Es kann aber durch die Logical Link Control (LLC)-Schicht erweitert werden, die auch verbindungsorientierte Kommunikation erlaubt. Dann ist die Sicherungsschicht in zwei Schichten, die MAC-Schicht und die LLC-Schicht zerlegt. Die Medium Access Control (MAC)-Schicht beinhaltet das Vielfachzugriffsverfahren. Die Logical Link Control (LLC)-Schicht bietet die verbindungsorientierte Kommunikation, die LLC ist durch IEEE 802.2 definiert. Ethernet ist sehr einfach und günstig und daher sehr weit verbreitet. Dies gilt insbesondere, da auch schnellere Varianten von Ethernet, Fast Ethernet und Gigabit Ethernet mit 100 bzw. 1000 MBit/s existieren. 7.3.6 Was wissen Sie über TCP/IP siehe 7.7.12 und 7.8.3. 82 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.4. Schicht 1 7.4 Schicht 1 7.4.1 Was gibt es für Medien? Kabel Coax Twisted Pair Lichtwellenleiter Multimode Fiber Monomode Fiber Drahtlos Funk Mikrowellenfunk Infrarot Laser 7.4.2 Wie funktionieren Lichtwellenleiter? Ein Lichtwellenleiter besteht aus einer sehr dünnen Glasfaser, die von einer Glasshülle umschlossen wird. Das ganze wird dann noch durch eine Kunsstoffhülle geschützt. Am einen Ende der Leitung sitzt eine Lichtquelle, zum Beispiel eine Laserdiode, am anderen Ende sitzt ein Detektor. Vereinbarungsgemäss steht Licht an für 1 und Licht aus für 0. Aufgrund der Brechungs- und Beugungsgesetze verlässt das Licht den Leiter nicht, sondern ist praktisch in ihm gefangen und kann wegen der minimalen Dämpfung über sehr weite Entfernungen übertragen werden. 7.4.3 Welche Arten von Lichtwellenleitern gibt es? siehe 7.4.1. 7.5 Schicht 2a 7.5.1 Welche Medienzugriffsverfahren kennen Sie? siehe 7.5.7. 7.5.2 Erklären Sie CSMA-CD. In der 1-persistenten Variante läuft CMSA/CD so ab: – sendewillige Station überwacht den Kanal (Carrier Sense) – ist der Kanal frei, wird gesendet – ist der Kanal belegt, wird er weiter überwacht bis er frei ist, dann wird sofort übertragen – während der Übertragung wird der Kanal weiter abgehört – bei Erkennung einer Kollision wird die Übertragung abgebrochen und ein JAM-Signal gesendet – danach wird entsprechend des Binary-Backoff-Algorithmus’ gewartet – jetzt geht es von Vorne wieder los 7.5.3 Wie funktioniert ALOHA? ALOHA ist ein Vielfachzugriffsverfahren ohne Reservierung und ohne Carrier Sense. Es gibt zwei verschiedene Arten von ALOHA. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 83 Kapitel 7. Rechnernetze pure ALOHA. Bei pure ALOHA kann jede Station senden, wann sie will, sie macht kein Carrier Sensing. Während des Sendens hört die Station weiter mit und kann dadurch erkennen, ob ihre Daten korrekt gesendet wurden oder nicht. Wenn es einen Fehler gab, sendet sie einfach nochmal. Die maximale Ausnutzung der Bandbreite liegt bei 18%. slotted ALOHA. Bei slotted ALOHA wird die Zeit in sog. Slots unterteilt. Um die Slots zu synchronisieren, kann z. B. eine Station als Taktgeber fungieren. Bei slotted ALOHA dürfen die Station nicht zu jeder Senden, sondern immer nur zu Beginn eines Slots. Durch slotted ALOHA kann die Leistung in etwa verdoppelt werden. 7.5.4 Was ist DQDB? DQDB ist in der IEEE Norm 802.6 definiert und beschreibt ein MAN. DQDB wird in der 4. Auflage des Tanenbaums nicht mal erwähnt, scheint also nicht mehr relevant zu sein. 7.5.5 Wie funktioniert FDDI? Fiber Distributed Data Interface ist ein Lichtwellenleiter basiertes Token-Ring-LAN, das 100 Mbps über Entfernungen von 200km unterstützt. FDDI wird in der 4. Auflage des Tanenbaums nur noch am Rande erwähnt. 7.5.6 Wie funktioniert die Manchester-Codierung? Codierungsverfahren, bei dem die binären Informationen durch Spannungswechsel innerhalb der Bitzeit dargestellt werden. Dadurch können Sender und Empfänger wesentlich leichter synchronisiert werden, denn der Übergang in der Mitte der Bitzeit gibt einen zuverlässigen Takt. Eine binäre 1 wird durch ein High-Signal gefolgt von einem Low-Signal dargestellt, ein 0 genau umgekehrt. Die ManchesterCodierung ist self-clocking , d.h. es muss kein extra Taktsignal gesendet werden. Der Empfänger kann den Takt selbst erkennen. Deshalb hat bei Ethernet jeder Frame eine 7 Byte lange, aus der Folge 10 bestehende, Präambel. Nachteil der Manchester-Codierung ist, dass die doppelte Bandbreite benötigt wird. 7.5.7 Geben Sie eine Einteilung der Vielfachzugriffsverfahren an. Vielfachszugriffsverfahren ohne Reservierung ohne Carrier Sense pure Aloha 7.5.8 slotted Aloha mit Reserverierung mit Carrier Sense p-persistent nonpersistent statisch TDM dynamisch FDM Token Wie funktioniert der Binary-Back-Off-Algorithm? Nach i Kollision wird eine Zufallszahl zwischen 0 und 2i − 1 gewählt. Entsprechend dieser Zufallszahl wird gewartet. Nach 10 Kollisionen wird die Wartezeit nicht mehr erhöht. Nach 16 Kollisionen meldet der Controller einen Fehler. 7.5.9 Was ist der Konfliktparameter? Eine Nachricht darf bei CSMA/CD nicht komplett in den Kanal passen, sonst kann eine Kollision auftreten, ohne dass die sendende Station das bemerkt. Betrachten wir den Worst-Case. 84 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.5. Schicht 2a Wir haben zwei Stationen A und B, die genau an den beiden Enden des Mediums liegen. Jetzt sendet A einen Frame FA . Kurz bevor der Frame am anderen Ende des Kabels ist, sendet die dortige Station einen Frame FB . B erkennt den Frame FA von A und sendet wegen der Kollision sofort ein JAM-Signal J. Dieses muss jetzt aber wieder über das ganze Kabel bis zu A. Wichtig ist das A zu den Zeitpunkt, in dem das JAM-Signal bei ihm eintrifft noch nicht mit dem Senden des Frames F A fertig ist, sonst würde sie nichts von der Kollision mit bekommen. Um das zu gewährleisten gibt man den Frames eine Mindestlänge, so dass eine sendende Station mindestens so lang mit senden beschäftigt ist, wie das Signal zum Ende des Leiters und das JAM-Signal zurück benötigt. Die maximale Kabellänge muss natürlich auch spezifiziert sein. Nehmen wir an die Signallaufzeit von einem Ende des Mediums zum anderen beträgt t Zeiteinheiten, dann muss eine Station zum Senden eines Frames mindestens 2 · t Zeiteinheiten benötigen, damit nichts schief geht. Der Konfliktparameter K beschreibt genau diesen Zusammenhang: K= 2·t ∆t Wobei ∆t die Nachrichtenübertragungszeit ist. Um das vorhergesagte zu formalisieren, fordern wir dass K= 2·t <1 ∆t immer gelten muss. Wenn wir für ∆t den Quotienten aus Framelänge l und Kanalübertragungsrate λ einsetzen, können wir die minimale Framelänge lmin berechnen. K lmin 7.5.10 = ⇓ > 2·t 2tλ = <1 ∆t l 2tλ Wie funktioniert Token Ring? Token Ring verwendet ein dynamisches Vielfachzugriffsverfahren mit Reservierung. Ein Token kreist in dem (physischen) Ring. Wenn eine Station senden will, muss sich den Token vom Ring nehmen. Nur wer einen Token hat darf senden. 7.5.11 Vergleiche Sie CSMA/CD und Token Ring/Bus? Da ein Token verloren oder kaputt gehen kann oder eine Station eine Token nicht mehr her geben kann, ist das Tokenmanagement von Token Ring/Bus ist deutlich aufwendiger als CSMA/CD. Siehe dazu auch 7.5.12. 7.5.12 Was passiert bei Token Ring, wenn eine Station ausfällt? Ein wirkliches Problem ergibt sich beim Ring, wenn das Kabel reisst. Falls eine Station ausfällt (oder abgeschaltet) wird entfällt auch die Verzögerung die sie normalerweise auf das Token hat, das Token muss dann eventuell an anderer Stelle stärker verzögert werden. Ein anderes Problem ist, dass es bei Token Ring immer eine Überwachungsstation gibt, die dafür u. a. dafür zuständig verloren gegangene Tokens zu ersetzen. Fällt diese aus, muss eine andere einspringen, was oft zu Problemen führt. 7.5.13 Was ist der Vorteil von Switched Ethernet? Die Kollisiondomänen sind kleiner, dadurch gibt es weniger Kollisionen. Die Sicherheit wird erhöht, da nicht jede Station den ganzen Datenverkehr mit bekommt. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 85 Kapitel 7. Rechnernetze 7.6 Schicht 2b 7.6.1 Was ist Sliding Window ? Sliding Window beschreibt ein Verfahren zur Flusskontrolle das flexibler als Stop-And-Wait ist. Die Idee ist folgende. Bei Stop-And-Wait muss der Sender immer auf die Bestätigung für einen Frame warten, bevor er den nächsten senden darf. Bei langen Signallaufzeiten wird dadurch sehr viel Kapazität verschwendet. Besser wäre es, wenn der Sender mehrere Frames verschicken kann und dann nach und nach die Bestätigungen bekommt. Grundvoraussetzung für Sliding Window ist, dass jeder Frame eine eindeutige Sequenznummer hat. Damit können Duplikate unterschieden werden. Sowohl der Sender wie auch der Empfänger haben ein Fenster von Sequenznummern, die bei beiden unterschiedliche Funktionen erfüllen. Diese Fenster heissen entsprechen Sendefenster und Empfangsfenster. Beide Fenster sind jeweils durch eine unter und obere Grenze begrenzt. Diese Grenzen sind Sequenznummern. Die Sequenznummer im Sendefenster sind die Frames die verschickt wurden oder verschickt werden dürfen, für die aber noch keine Bestätigung eingegangen ist. Wenn ein Paket von der Netzwerkschicht kommt wird ein Frame mit der nächsten höchsten Sequenznummer erstellt und die obere Grenze des Sendefensters wird um eins erhöht. Dadurch verwaltet das Sendefenster immer eine List von noch nicht bestätigten Frames. Da theoretisch all diese Frames verloren gehen könnten, muss der Sender von jedem eine Kopie speichern. Dafür braucht er Speicherplatz und daher ist Grösse des Fensters begrenzt. Falls die Grösse des Fensters erschöpft ist und die Netzwerkschicht weiterhin versucht neue Pakte zu verschicken, muss die Sicherungsschicht der Netzwerkschicht signalisieren, sich zu gedulden und keine neuen Pakete mehr anzuliefern, bis wieder Platz im Sendefenster entstanden ist. Der Sender muss ausserdem aufhören zu Senden, wenn ihm die Sequenznummern ausgehen. Wenn z. B. 3 Bits für die Sequenznummer verwendet werden, kann er 8 Frames verschicken ohne eine Bestätigung zu bekommen, dann muss er auf eine Bestätigung warten. Das Empfangsfenster entspricht den Frames die der Empfänger bereits ist anzunehmen. Alle ankommenden Frames, die ausserhalb des Fensters liegen, werden sofort verworfen. Wenn ein Frame ankommt, dessen Sequenznummer der unteren Grenze des Empfangsfensters entspricht, wird dieser an die Netzwerkschicht weitergegeben, eine Bestätigung verschickt und das ganze Empfangsfensters (ober und untere Grenze) um eins nach oben geschoben (rotiert). In der Bestätigung, die an den Sender geschickt wird, steht die Sequenznummer des letzten korrekt erhaltenen Frames, so weiss der Sender, ob alles klar gegangen ist und kann das nächsten Frame verschicken. Falls der Empfänger einen Frame empfängt, der nicht die aktuell erwartete Sequenznummer hat, kann er nach zwei Strategien vorgehen. Die einfachere wird go back n genannt. Der Empfänger akzeptiert die Frames einfach nicht und sendet keine Bestätigungen (dieses Verfahren ergibt sich automatisch, wenn die Grösse des Empfangsfensters 1 ist). Die aufwendigere Methode nennt sich selective repeat. Wenn der Empfänger einen Fehler entdeckt, schickt er dem Sender eine negative Bestätigung (NAK) und sagt ihm genau, welcher Frame fehlt, der Sender kann diesen dann nochmal schicken. Erst wenn der Empfänger eine vollständige Sequenz von Frames hat, gibt er diese an die Netzwerkschicht weiter. Wichtig ist, dass die Reihenfolge der Frames beim Senden zwar durcheinander kommen kann, aber der Netzwerkschicht die Pakete immer in der richtigen Reihenfolge übergeben werden. 7.6.2 Welche Verfahren zur Flusssteuerung kennen Sie? Zur Flusskontrolle gibt es feedback-basierte Mechanismen und rate-basierte Mechanismen, in der Sicherungsschicht werden aber nur feedback-basierte Mechanismen eingesetzt. Zwei Protokolle sind StopAnd-Wait und Sliding Window. Sliding Window wird in 7.6.1 beschrieben. Stop-And-Wait ist sehr einfach. Der Sender sendet einen Frame, dann wartet er solange, bis er eine Bestätigung vom Empfänger bekommen hat. Erst dann sendet er den nächsten Frame. 7.6.3 Wie teilt der Empfänger bei dem Sender Sliding Window mit, dass er mit dem Senden aufhören soll? Wenn der Empfänger überfordert ist, sendet er keine Bestätigungen mehr. Dadurch muss der Sender früher oder später aufhören zu Senden, da sein Sendefenster erschöpft ist. Bei HDLC (7.6.7) kann der Empfänger einen Supervisory-Frame vom Typ Recieve Not Ready an den Sender schicken. 86 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.6. Schicht 2b Bei TCP schickt der Empfänger dem Sender immer die Grösse seines Empfangsfensters mit, wenn er überfordert ist, schickt er eine 0. 7.6.4 Wie funktioniert CRC? Die Grundidee von CRC ist, dass man eine Folge von n Bits als ein Polynom vom Grad n − 1 dessen Koeffizienten nur 0 oder 1 sind, betrachten kann. Polynomielle Arithmetik wird modulo 2 durchgeführt, daher entspricht Addition und Subtraktion einem einfach XOR. Das Prüfsummenverfahren läuft nun wie folgt ab. Sender und Empfänger einigen sich auf eine Generatorpolynom G(x), wobei das höchste und das niedrigste Bit 1 sein muss. Um einen Frame mit m Bits, die dem Polynom M (x) entspricht zu verschlüsseln, muss der Frame länger sein als das Generatorpolynom. Nun hängt man an den eigentlichen Frame eine Checksumme an, so dass das gechecksummte Frame durch G(x) teilbar ist. Wenn der Empfänger eine Frame erhält versucht er ihn durch G(x) zu teilen. Gibt es einen Rest, liegt ein Übertragungsfehler vor. Dadurch ergibt sich folgender Algorithmus zur Berechnung der Checksumme: 1. r sei der Grad von G(x). An den Frame werden auf der niederwertigen Seiten r 0-Bits angehängt. Er hat jetzt m + r Bits und entspricht dem Polynom xr M (x). 2. Diese Polynom xr M (x) wird nun entsprechen der Modulo-2-Division durch G(x) geteilt. 3. Dabei ergibt sich ein Rest von r oder weniger Bits. Dieser wird nun von Bitstring der x r M (x) entspricht entsprechend der Modulo-2-Subtraktion abgezogen. Das ist nun der gechecksummte Frame, der T (x) genannt wird. Es stellt sich die Frage, warum T (x) durch G(x) teilbar ist. Das ist eine fundamentale Eigenschaft der Division, es gilt immer: Divisor | Dividend − Rest Zum Beispiel gilt für 9/4 mit Rest 1: 4 | 9 − 1. Die Güte der Checksumme ist bei CRC abhängig vom Grad des Generatorpolynoms. Allgemein gilt mit einem Generatorpolynom vom Grad k findet man alle Bit-Fehler der Länge ≤ k. Wenn man das Polynom geschickt wählt kann man ausserdem noch alle Bitfehler die eine ungerade Anzahl an Bits betreffen finden. Das liegt daran, dass kein Polynom mit einer ungeraden Anzahl an Termen das Polynom x2 + 1 als Faktor hat. Wenn x2 + 1 ein Faktor des Generatorpolynoms ist, dann findet man die ungeradlängigen Bitfehler. Es gibt einige international standardisierte Polynome. Ethernet verwendet das folgende Polynom vom Grad 3 x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x1 + 1 Die Berechnung der CRC kann in Hardware sehr effizient gelöst werden. 7.6.5 Welche Fehler werden durch CRC erkannt? siehe 7.6.4. 7.6.6 Wie sichert man Nachrichteninhalt? Mit Checksummen, z. B. CRC (siehe 7.6.4). 7.6.7 Was ist HDLC? High-Level Data Link Control ist ein älteres bitorientiertes Sicherungsprotokoll das vor allem in öffentlichen Paketvermittlungsnetzen eingesetzt wird. Das Protokoll nutzt ein Flag und bit-stuffing um die Frames voneinander zu trennen. Zur Flusskontrolle wird ein Sliding Window verfahren mit einer 3 Bit Sequenznummer verwendet. Es gibt drei verschiedene Arten von Frames: Information, Supervisory und Unnumbered. Wobei Information-Frames für normale Datenübertragung, Supervisory-Frames für Signalisierung und die Unnumbered-Frames für verschieden Zwecke verwendet werden. Unnumbered-Frames können für verbindungslose Kommunikation benutzt werden. Die Supervisory-Frames können verschiedene Typen haben. Wichtige Typen sind Recieve Ready für normale Bestätigung und Reject für negative Bestätigung. Zusätzlich gibt es Recieve Not Ready um den Sender mitzuteilen, dass der Empfänger nicht mehr mitkommt und Selective Reject um einen Fehler für einen bestimmten Frame anzuzeigen. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 87 Kapitel 7. Rechnernetze 7.7 Schicht 3 7.7.1 Erklären Sie, was Schicht 3 macht. siehe 7.2.1. 7.7.2 Was sind statische und adaptive Routing-Verfahren? Bei statischen Routing-Verfahren werden die Wegewahlentscheidungen im Vorhinein ohne bestimmt. Adaptive Routing-Verfahren passen die Entscheidungen mit Messungen an die aktuellen Gegebenheiten an. 7.7.3 Ist auf Schicht 3 eine zuverlässige Übertragung möglich? Schicht 3 kann sowohl verbindungsorientiert als auch verbindungslos sein. IP ist eine Beispiel für eine verbindungslose Technik, ATM für eine verbindungsorientiert. 7.7.4 Wie funktioniert Routing im Internet? In autonomen Subnetzen (AS) wird heute hauptsächlich OSPF eingesetzt. Zwischen den AS meisten BGP. 7.7.5 Erklären Sie Shortest-Path-First-Routing. Beim Shortest-Path-Routing werden die kürzesten Pfade im Netzwerk mit einem typischen Algorithmus wie Dijkstra bestimmt. Das Kantengewicht kann auf verschiedene Arten gewählt werden. Es kann einfach immer 1 betragen, den zählt man nur die Hops oder man wählt andere Faktoren wie die physische Distanz, Übertragungszeit oder die durchschnittlichen Queueingzeiten. Auch eine Kombination der Faktoren ist möglich. Die Routingentscheidungen werden dann entsprechend der kürzesten Wege bestimmt. 7.7.6 Erklären Sie Distance-Vector-Routing. Distance-Vector-Routing ist ein dynamischer Algorithmus, der wie folgt funktioniert. Jeder Router verwaltet einen Vektor mit den besten bekannten Entfernungen zu jedem Ziel und der entsprechenden Leitung. Diese Tabelle werden immer wieder geupdatet in dem mit den Nachbarn Informationen ausgetauscht werden. Für jeden Zielrouter enthält der Vektor einen Eintrag mit der zu wählenden Ausgangsleitung und einen Eintrag für die geschätzte Entfernung zum Ziel. Mögliche Metriken hier sind Hops, Übertragungszeit oder Queueingzeiten. Jeder Router kennt die Entfernung zu seinen Nachbarn, falls die Metrik nicht Hops ist, kann der Router die Entfernung anhand einer Analyse seiner Queue oder durch Messung von Signallaufzeiten bestimmen. In einem bestimmten Zeitintervall verschickt jeder Router die von ihm geschätzten Zeiten an seine Nachbarn. Mit den neu erhaltenen Tabellen der Nachbarn kann jeder Router entscheiden, ob es bessere Wege zu manchen Zielen gibt, als in seinem Vektor gespeichert ist. Distance-Vector-Routing hat ein grosses Problem, es findet zwar eine gute Konfiguration, aber sehr langsam. Das Problem ist vor allem, dass sich gute Nachrichten sehr schnell, schlechte aber sehr langsam verbreiten. Das ist als Count-to-Infinity-Problem bekannt. Der Kern des Problems liegt daran, dass wenn X Y mitteilt, dass er irgendwohin einen Pfad hat, Y nicht weiss, ob er selber auf dem Pfad liegt oder nicht. 7.7.7 Erklären Sie Link-State-Routing. Link-State-Routing ist adaptiver Routing-Algorithmus bei dem Informationen über die Netztopologie und alle Verzögerungszeiten gesammelt und an jeden Router verteilt wird. Dann können mit dem Dijkstra-Algorithmus die kürzesten Pfade bestimmt werden. Jeder einzelne Router geht nach folgenden Schritten vor: 88 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.7. Schicht 3 1. Nachbarn entdecken und deren Adressen merken. Als erstes Sendet der Router über jede seiner Leitungen ein Hello-Paket. Als Antwort erhält er vom Router auf der anderen Seite dessen Adresse. Diese Adressen müssen eindeutig sein. 2. Die Verbindungskosten zu jedem Nachbarn messen. Jetzt sendet er an jeden Nachbarn ein Echo-Paket um die Verzögerung zu messen. Er kann dies mehrmals durchführen und einen Durchschnittswert wählen. Ob hierbei die Auslastung der Leitung mit in die Kalkulation aufgenommen wird, ist ein Diskussionspunkt. 3. Ein Paket zusammenstellen, dass die gerade gelernten Informationen enthält. Jetzt baut der Router ein Paket mit den gewonnen Informationen zusammen. Diese Paket enthält die Adresse des Router, eine Sequenznummer, ein Alter und eine Liste der Nachbarn. Für jeden Nachbarn enthält es die gemessene Verzögerungszeit. Die Frage ist nur, wann die Pakete verschickt werden. Dies kann entweder periodisch erfolgen oder wenn ein besonderes Ereignis, zum Beispiel ein nicht erreichbarer Router, auftritt. 4. Dieses Paket an alle anderen Router schicken. Das verschicken des Pakets ist er komplizierteste Teil, das sich Inkonsistenzen ergeben können, wenn manchen Router schon die neuen Information erhalten haben und andere nicht. Grundsätzlich basiert das Verfahren bei der Verteilung auf Flooding. Um das Flooding kontrollieren zu können, versieht der Router jedes Paket, das er versendet mit einer neuen Sequenznummer. Jeder Router speichert eine List von Paaren (Router, Sequenznummer), die er gesehen hat. Wenn ein neuen Paket ankommt, prüft er ob dieses neu ist, wenn ja verschickt er es an alle Router ausser dem Quellrouter. Wenn es ein Duplikat ist, verwirft er es. Wenn er ein Paket mit einer niedrigeren Sequenznummer als die höchste, die der Router bisher gesehen hat, ankommt, lehnt er das Paket ab, weil es alt ist. Es ergeben sich ein Paar Probleme, die aber in den Griff zu bekommen sind. Als erstes müssen die Sequenznummern am besten 32-bitig sein, damit sie nicht überlaufen. Wenn ein Router ausfällt und seine Sequenznummer vergisst, daher wieder bei 0 anfängt, werden seine Pakte nirgendwo mehr angenommen. Falls eine Paket kaputt geht und z. B. statt der Sequenznummer 4 eine 65.540 enthält, was auf einen 1-Bitfehler zurückzuführen ist, werden alle Pakete zwischen Sequenznummer 5 und 65.540 nicht mehr angenommen. Alle Probleme können mit dem Age-Feld der Pakete behoben werden. Das Alter eines bereits empfangenen Paketes wird jede Sekunde um 1 herunter gezählt. Paket mit Alter 0 sterben. Das Alter wird am Anfang so gesetzt, das normalerweise immer ein neues Paket vom entsprechenden Router ankommt, bevor es stirbt. Ausserdem wird beim Flooding am Anfang das Alter bei jedem Hop herunter gezählt, damit Paket nicht verloren gehen können. 5. Kürzeste Pfade zu allen Router berechnen. Wenn ein Router die komplette Menge an Link-State-Paketen bekommen hat, kann er daraus die Topologie des kompletten Netzes erschliessen und mit Hilfe von Dijkstra die kürzesten Pfade berechnen. Der Algorithmus funktioniert sehr gut und wird zum Beispiel bei OSPF eingesetzt. Defekte Hardoder Software kann den Algorithmus aber empfindlich stören. Wenn z. B. ein Router behauptet eine bestimmte Verbindung zu haben, wird der Graph auf dem die Berechnung basiert, inkorrekt. 7.7.8 Erklären Sie OSPF. OSPF ist ein Internet Routing Protokoll für autonome Subnetze, es wird ein Link-State-RoutingAlgorithmus verwendet, siehe dazu 7.7.7. 7.7.9 Nennen Sie ein nicht-adaptives, isoliertes Routing-Verfahren. Ich denke, hier ist Broadcasting gemeint. 7.7.10 Was ist RIP? RIP ist ein älteres Internet Routing Protocol, es verwendet Distance-Vector-Routing, siehe dazu 7.7.6. 7.7.11 Unterschied zwischen IPv4 und IPv6 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 89 Kapitel 7. Rechnernetze Die wichtigsten Unterschiede zwischen IPv4 und IPv6 sind IPv6 benutzt 16 Byte Adresse, das entspricht ca. 3, 4 · 1038 Adressen. Das Adressproblem ist damit behoben. Die Unterstützung für Optionen wurde verbessert. Zum einen dadurch, dass das Optionsfeld im IP-Header grösser ist, zum anderen werden die Information jetzt so gespeichert, das Router über Informationen, die nicht für sie bestimmt sind, einfach hinweg springen können. IPv6 hat verbesserte Unterstützung für Quality of Service. IPv6 hat Unterstützung für diverse Sicherheitsmechanismen wie Authentifizierung. 7.7.12 Erzählen Sie etwas zum IP-Protokoll. Das IP-Protokoll ist ein verbindungsloses Schicht 3 Protokoll. Zur Adressierung werden die bekannten IP-Adressen verwendet. Jedes IP-Paket besteht aus einem Header- und einem Datenteil. IP Pakete können bis 65.535 Bytes lang sein, sind aber in der Praxis meist 1500 Byte lang. 7.7.13 Wozu braucht man das TTL-Feld im IP-Header? Das Time to Live-Feld im IP-Header wird bei jedem Hop runtergezählt, damit z. B. zyklisch geroutete Paket nicht ewig Kreisen (bei TTL=0 stirbt das Paket). 7.7.14 Was ist Flooding? Flooding ist ein sehr einfaches statisches Routingverfahren bei dem jedes ankommende Pakete über alle Leitung aus der Quellleitung weitergeleitet wird. Damit Flooding nicht ausartet, muss es irgendwie eingedämmt werden, entweder durch Hopcounter oder es werden Sequenznummern vergeben, so dass ein Router ein bereits einmal gefloodetes Paket nicht nochmal floodet. Flooding ist meist nicht praktikabel findet aber doch seine Anwendung. Zum einem ist wahnsinnig robust, selbst wenn fast alle Netzknoten ausfallen und nichts mehr funktioniert, tut Flooding noch seinen Dienst. Ausserdem ist Flooding der Prüfstein für alle anderen Routingalgorithmen, da Flooding dadurch, dass es alle Wege wählt, immer den kürzesten Weg wählt. 7.7.15 Was ist hot potatoe? Beim Hot-Potatoe-Routing können Pakete nicht gespeichert werden, sondern werden wie eine heisse Kartoffel weitergegeben. Kommt z. B. in Backbone-Netzen vor wenn ein Carrier Daten von anderen Carriern möglichst schnell wieder los werden will. Wird auch bei optischem Routing verwendet, wo Lichtsignale nicht zwischengespeichert werden können. 7.7.16 Was ist Staukontrolle? Im Gegensatz zur Flusskontrolle die dafür da ist, das ein Sender einen Empfänger, die beide an einer Punkt zu Punkt Verbindung hängen, nicht mit Daten überflutet, wird beim Thema Staukontrolle das komplette Netz betrachtet. Ein Stau existiert immer dann, wenn die Belastung des Netzes grösser ist als seine Ressourcen. Im Falle eines Staus können entweder die Ressourcen erhöht werden, oder, wenn das nicht geht, die Belastung gedrosselt werden. Generell unterscheidet man bei Staukontrolle zwischen Open Loop- und Closed Loop-Methoden. Open Loop Methoden. Open Loop Methoden versuchen das Problem durch gutes Design zu lösen, das heisst durch gute Planung im Vorhinein, während des Betriebs wird nicht mehr eingegriffen. Möglichkeiten den Stau im voraus zu vermeiden bestehen an mehreren Stellen. Data Link Layer. In der Sicherungsschicht sollte die Flusskontrolle unnötige Frames verhindern, also z. B. selective repeat und piggybacking einsetzen. Network Layer. In der Netzwerkschicht ist vor allem die Entscheidung zwischen verbindungsorientierten und verbindungslosen Diensten ausschlaggebend, da viele Staukontrolle-Algorithmen nur mit Verbindungen arbeiten. Ausserdem ist Wahl eines Routing-Algorithmus’, der die Last gut verteilt, wichtig. 90 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.8. Schicht 4 Transport Layer. Auch in der Transportschicht muss die Flusskontrolle unnötig versandte Daten vermeiden. Closed Loop Methoden. Closed Loop Methoden reagieren auf den aktuellen Zustand des Netzes, dieser Ansatz besteht immer aus drei Teilen: 1. Überwachung des Netzes. Wo und wann treten Verstopfungen auf? 2. Verteilung der Stauinformation an relevante Stellen. 3. Behebung des Problems Die folgenden Verfahren lassen sich nur bei verbindungsorientierter Kommunikation einsetzen. Wenn ein Stau entdeckt wurde, werden keine neuen Verbindungen mehr aufgebaut (Admission Control ). Verbindungen können vorsichtig um heikle Stelle herumgeführt werden. Es können bereits beim Verbindungsaufbau Verträge abgeschlossen werden. So fordert ein Kunde zum Beispiel eine gewisse Bandbreite, die Verbindung wird nur aufgebaut, wenn diese Bandbreite auch zur Verfügung steht. Im verbindungslosen Netzen muss anders vorgegangen werden. Ein Router kann die Auslastung seiner Ports messen, dieser Auslastung kann er eine Zahl zwischen 0 und 1 zuordnen. Jetzt wird eine Auslastungsobergrenze festgelegt. Wann immer ein Router ein Paket auf einem Port rausschicken soll, dessen Auslastung die Obergrenze übersteigt, muss er irgendwie reagieren. Dafür gibt es mehrere Möglichkeiten: Warning Bit. Der Router kann in ausgehenden Pakete ein Warning Bit auf 1 setzen, die Zielstation setzt dann ihrerseits ein Warning Bit in dem zu sendenden Bestätigunspaket, dass wieder zum Sender zurück läuft. Dieser drosselt daraufhin das Senden. Das dauert zwar relativ lang, hat aber den Vorteil, dass nicht in der schon bestehenden Stausituation noch extra Pakete versandt werden müssen. Choke Packets. Wenn der Router eine Überlastung feststellt sendet er ein Choke Packet an den Sender, der daraufhin drosselt. Hop-by-Hop Choke Packets. Es kann u. U. recht lange dauern, bis das Choke Packet beim Sender angekommen ist. Mit einem Hop-by-Hop Choke Packet kann der Router den Router vor ihm bitten, den Datenverkehr durch Pufferung zu drosseln. Dieser bittet wieder seinen Vorgänger usw. Load Shedding. Wenn die vorangegangen Methoden alle nichts Helfen, kann der Router zu brutaleren Methoden greifen, er verwirft einfach einen Teil der Pakete. Wenn der Router eine Ahnung von der Art der Daten hat (z. B. Multimedia) kann er erst einmal möglichst unwichtige Pakete verwerfen. Random Early Detection. Bei einigen Transportprotokollen (z. B. TCP) reagiert der Sender bei verloren Pakete mit einer Drosselung der Sendegeschwindigkeit. Das liegt daran, dass in kabelbasierten Netzen, für die z. B. TCP hauptsächlich entworfen wurde, fehlende Bestätigung meist auf Pufferüberläufe zurückzuführen sind. Wenn dem so ist, kann der Router, wenn er merkt, dass ein Stau kurz bevor steht manche Pakete der Quelle die die hohe Auslastung erzeugt, einfach verwerfen. Die Quelle reagiert dann mit der Drosselung der Sendeleistung. Das verfahren heisst early weil der Router das tut, bevor alles verstopft ist und random weil er aus der Queue in der er die Überlastung feststellt, zufällig ein Paket wählt und dieses verwirft. Dadurch wählt er den zu drosselnden Sender zufällig. 7.8 Schicht 4 7.8.1 Was ist das Transportsystem? Unter dem versteht man die Schichten 1-4. 7.8.2 Ist Schicht 4 immer verbindungsorientiert? Nein. UDP z. B. ist nicht verbindungsorientiert. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 91 Kapitel 7. Rechnernetze 7.8.3 Erzählen sie etwas zu TCP. TCP ist das verbindungsorientiert Schicht 4 Protokoll der Internetprotkollfamilie. Eine TCP-Verbindung wird immer von einem Socket zu einem anderen aufgebaut. Ein Socket ist eine IP-Adresse plus Port. Eine TCP-Verbindung ist Byte basiert, nicht Nachrichten basiert. Der Verbindungsaufbau läuft bei TCP über den Three-Way Handshake (7.8.4). Zur Flusskontrolle verwendet TCP ein Sliding Window Verfahren. Bei dem von TCP verwendeten Verfahren teilt der Empfängen dem Sender immer die Grösse seines Empfangsfensters mit. Wenn er es auf 0 setzt, kann er damit den Sender drosseln. Die ursprüngliche Version unterstützt kein Selective Repeat sondern nur go back n. Interessant ist, dass die TCP-Checksumme ausser dem Header und den Daten noch einen Pseudoheader mit ein berechnet. Dieser Pseudoheader enthält Ziel- und Quelladresse. Diese Verwendung von IP-Adressen auf Schicht 4 verletzt die Protkollhierarchie. Bei der Trennung der Verbindung werden Timer benutzt um das Two Army-Problem zu umgehen. Die Staukontrollemechanismen von TCP beruhen auf der Annahme das Pakete hauptsächlich wegen Pufferüberläufen und nicht wegen schlechter Leitungsqualität verloren gehen. Dies führt bei unsicheren Funkverbindungen zu Problemen. Zur Staukontrolle verwendet verwaltet der Sender neben dem Sendefenster zusätzlich ein Congestion Window. Dieses Fenster kann an die aktuelle Belastung des Netzes angepasst werden. 7.8.4 Was ist der Three-Way Handshake? Beim Aufbau einer TCP-Verbindung muss – bevor eine Verbindung zustande kommt – zunächst eine Verständigung zwischen Sender und Empfänger stattfinden. Dazu verwendet TCP die Methode des Three-Way Handshake. Das heißt es müssen zunächst drei TCP-Pakete gesendet werden bevor die Verbindung aufgebaut ist. Erst mit einem erfolgreichen letzten und drittem Segment erfolgt der Handschlag und die Verbindung besteht. Angenommen man hat Host A, der eine Verbindung mit Host B, aufnehmen möchte, passiert folgendes: 1. Im ersten Paket von A nach B: ACK-Flag SYN-Flag Sequenznummernfeld Acknowledgementfeld = = = = 0 1 initiale Sequenznummer nicht benutzt 2. Möchte B die Verbindung aufnehmen schickt er ein Paket nach A ACK-Flag SYN-Flag Sequenznummernfeld Acknowledgementfeld = = = = 1 1 initiale Sequenznummer Sequenznummer von A inkrementiert 3. Handschlag von A nach B ACK-Flag SYN-Flag Sequenznummernfeld Acknowledgementfeld = = = = 1 0 initiale Sequenznummer inkrementiert Sequenznummer von B inkrementiert Nach diesen drei Schritten ist die Verbindung aufgebaut. Möchte man den Aufbau einer TCP-Verbindung unterbinden muss man das erste Paket heraus filtern. So wird der weitere Aufbau einer Verbindung unmöglich. Das ACK-Flag spielt hier die entscheidende Rolle, da dieses Flag nur im ersten Paket nicht gesetzt ist. So werden bestehende TCP-Verbindungen nicht beeinträchtigt, da hier in allen Paketen das ACK-Flag gesetzt ist. Die Abbildung zeigt den Three-Way Handshake in seiner üblichen Darstellung. A, S sind das ACK bzw. SYN -Flag, SEQ und ACK sind das Sequenznummernfeld und Acknowledgementfeld. 92 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 7.8. Schicht 4 Host 1 Host 2 ASS 0 1 EQ ACK x Time CK EQ A A S S y x+1 11 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs ASS 1 0 x EQ ACK +1 y +1 93 Kapitel 8 Rechnernetze II 8.1 Allgemeines 8.2 Hochleistungsnetze 8.2.1 Gibt es bei ATM MAC-Adressen? ATM verwendet sein eigenes Adressformat. Genauer gesagt gibt es drei verschiedene Adressformate, die alle 20 Byte lang sind. Die Abbildung zeigt das DCC-Format. 1 AFI 2 3 4 5 6 7 8 DCC 9 10 HO-DSP 11 12 13 14 15 16 17 ESI 18 19 20 SEL Im ersten Byte, dem Authority Format Identifier (AFI) wird das Adressformat angegeben, der Wert 39 steht für das DCC-Format. In den folgenden zwei Bytes steht der Data Country Code (DCC). Er bestimmt das Land, 276F steht für die BRD. Byte 4 bis 13 werden vom High Order Domain Specific Part (HO-DSP) belegt. Hier sind die Routing Domain und der Area Identifier abgelegt. Den Rest der 20 Byte teilen sich der End System Identifier (ESI) und das Selector Field (SEL). Wobei dem ESI die Rolle einer MAC-Adresse bei einem Ethernet-Gerät zukommt. Das SEL spielt eine ähnliche Rolle wie Ports bei IP. Wenn auf einer Maschine mehrere Dienste (z. B. BUS und LECS) laufen, so werden sie durch das SEL unterschieden. 8.2.2 Wie funktioniert bei ATM die Vermittlung? Ein Vermittler liest die VPI/VCI-Information der ankommende Zelle aus dem Header und routet die Zelle anhand von Tabellen. In internen Vermittler (Vermittler, die keine Verbindung zum einem Host haben) kann das Routing nur in Abhängigkeit von der VPI-Information gemacht werden. Das hat mehrere Vorteile. Der Vermittler muss verhältnismässig kleine Tabellen speichern. Dass Routing ist schneller und Routen lassen sich sehr schnell für viele VCs umleiten. 8.2.3 Geben Sie einen kurzen Überblick über ATM. ATM (Asynchronous Transfer Mode) ist eine verbindungsorientierte Netzwerktechnologie, die Datenraten bis 622 MBit/s bietet. Die Verbindungen heissen Virtual Circuits (VC) und können entweder permanent oder switched sein. Mehrere VCs können zu einem Virtual Path (VP) zusammengefasst werden. Das bietet die Möglichkeit, eine ganze Sammlung von VCs zu routen. Ausserdem können QoS-Parameter einem ganzen Pfad zugeordnet werden. ATM setzt statt Pakete Zellen von 53 Byte Länge, die einen 5 Byte Header haben ein. Welche Vorteile der Einsatz von Zellen hat, beschreibt 8.2.6. ATM-Verbindungen bieten keine garantierte Zellzustellung, aber alle Zellen werden in der richtigen Reihenfolge zugestellt. Das ATM-Modell lässt sich im Gegensatz zum OSI-Schichtenmodell besser dreidimensional darstellen. Die Benutzerebene betrifft Datentransport, Flusssteuerung, Fehlerkorrektur und andere Benutzerfunktionen. Die Steuerebene beschäftigt sich mit dem Verbindungsmanagement. Ebenen- und Schichtenmanagement sind für Ressourcenmanagement und Schicht übergreifende Koordination zuständig. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 95 Kapitel 8. Rechnernetze II Ebenenmanagement Schichtenmanagement Steuerebene Obere Schichten CS SAR Benutzerebene Obere Schichten ATM-Anpassungsschicht ATM-Schicht TC PMD Physische Schicht Die Physische Schicht und die Anpassungsschicht bestehen aus zwei Teilschichten, wobei jeweils die untere die Arbeit tut und die oberen für die Schnittstelle nach oben zuständig ist. Die ATM-Anpassungsschicht (ATM Adaption Layer, AAL) hat die Aufgabe die Datenströme der höheren Schichten auf 48-Byte Lange Zellen zu segmentieren, bzw. aus den ATM-Zellen wieder die ursprünglichen Datenpakete zu erstellen (Reassembly). Es gibt verschiedene Typen der ALL-Schichte, derzeit sind AAL 1–5 spezifiziert. Je nach Anwendung (z. B. Clip) wird eine andere Schicht gewählt. Die Hauptaufgabe der ATM-Sicht besteht darin, die von der AAL-Schicht empfangenen Daten an ihren Bestimmungsort zu transportieren. Die Informationseinheit der ATM-Schicht sind die 53-Byte Zellen. Die ATM-Schicht ist vollkommen unabhängig von der unter ihr liegenden physischen Schicht. Die physische Schicht ist für die Generierung der ATM-Zellen, Checksummen-Berechnung und das tatsächliche Versenden zuständig. Für eine genauer Beschreibung der Funktion der Schichten siehe auch 8.2.4. 8.2.4 Vergleichen Sie das ATM-Schichtenmodell mit dem ISO–OSI–Schichtenmodell. Physische und AAL-Schicht (AAL = ATM Adaption Layer) sind jeweils zweigeteilt. Wobei die eigentliche Arbeit in der unteren Schicht stattfindet und die Konvergenzschicht darüber die Schnittstelle nach oben bietet. Die Tabelle gibt einen Überblick über die Schichten des ATM-Modells und zeigt auch die korrespondierenden Schichten im OSI-Modell. Der Versuch die Schichten von ATM in das OSI-Schichtenmodell einzuordnen ist nicht unproblematisch. OSI ATM Teilschicht Funktionalität 3/4 AAL CS (Convergence Sublayer) SAR (Segmentation and Reassembly Sublayer) 2/3 ATM 2 Physisch Bereitstellung der Standardschnittstelle (Konvergenz) Segmentierung und erneute Zusammensetzung Flusssteuerung Erzeugung/Extraktion des Zellheaders Management des virtuellen Pfades Multiplexen/Demultiplexen der Zellen Entkoppelung der Zellrate Erzeugung der Header-Prüfsummen Erzeugung der Zellen Ein-/Auspacken der Zellen Erzeugung von Rahmen Bitzeitgabe Physischer Netzzugriff 1 8.2.5 96 TC (Transmission Convergence Sublayer) PM (Physical Medium Dependent Sublayer) i Wie sieht die Sicherungsschicht bei ATM aus? http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 8.3. Dienstgüte Die Sicherungsschicht des OSI-Modells entspricht einer Mischung der Transmission Convergence Layer und der ATM-Schicht. Siehe 8.2.4. 8.2.6 Bei ATM-Zellen hat man einen relativen hohen Protokoll-Overhead, warum macht man das? Der wichtigste Grund ist, dass sich durch die kleine Zellengrösse die Zellen schneller (eventuell parallel) verarbeiten lassen. Zellrouting wird in Hardware realisiert. Da Pakete (im Verhältnis zu Zellen) sehr gross werden können, müssen neu ankommende Pakete, die übertragen werden sollen, im Mittel sicherlich länger warten. Bei Übertragungsfehlern muss immer ein ganzes Paket neu übertragen werden. Wenn nur an einer Stelle eines Pakets ein Fehler aufgetreten ist, müssen u. U. 64k übertragen werden. Das ist ungefähr 1200 mal so viel wie bei einer ATM-Zelle. Durch die feste Zellengrösse die lassen sich genauer Werte über z. B. die Belegungsdauer genauer bestimmen und vorhersagen. 8.2.7 Würden Sie ATM lokal einsetzen? Im Vergleich zu Ethernet kann ATM hauptsächlich noch durch QoS punkten. Wenn QoS tatsächlich die zentrale Anforderung an das Netz ist (z. B. Video-Übertragung), dann könnte ATM eine Überlegung wert sein. Zu Berücksichtigen ist, dass ATM sehr teuer und sehr aufwendig zu konfigurieren ist. Ausserdem lässt es sich sehr schlecht in eine IP-Umgebung einbauen, wenn man nicht auf QoS verzichten will, oder man muss eine noch aufwendigere manuelle Konfiguration vor nehmen. Mit derzeitigen LAN-Technologien bekommt man eventuell für das selbe Geld ein so dermassen überdimensioniertes Gigabit-Ethernet, dass sich QoS von alleine erledigt. 8.3 Dienstgüte 8.3.1 Was ist QoS? Dienstgüte (Quality of Service) bezeichnet quantifizierbare Eigenschaften eines Dienstes. Die einzelnen Eigenschaften werden als Dienstgüteparameter oder Merkmale bezeichnet. Die wichtigsten Merkmalsklassen sind: Bandbreite (Bandwidth). gemessen in Bit/s. Verzögerung (Delay). gemessen in ms. Jitter. Jitter ist die Standardabweichung der Verzögerung. Zuverlässigkeit (Reliability). Hier interessiert sowohl die Zerstörung als auch der Verlust von Daten. Unterschiedliche Anwendungen haben unterschiedliche Anforderungen an die Merkmalsklassen, die Tabelle gibt einen Überblick. Anwendung eMail Dateitransfer WWW Remote Login Audio niedrig Video niedrig Telephonie niedrig Videokonferenz niedrig 8.3.2 Zuverlässigkeit hoch hoch hoch hoch niedrig niedrig niedrig niedrig Verzögerung niedrig niedrig mittel mittel niedrig niedrig hoch hoch Jitter niedrig niedrig niedrig mittel hoch hoch hoch hoch Bandbreite niedrig mittel mittel niedrig mittel hoch niedrig hoch Welche Strategien gibt es für QoS? Overprovisioning. Man stellt einfach so viele Ressourcen zur Verfügung, dass alle QoS-Bedürfnis von selbst erfüllt werden. Das Problem bei dieser Lösung ist der Kostenfaktor. Das normale Telefonnetz ist ein Beispiel für diese Lösung. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 97 Kapitel 8. Rechnernetze II Buffering. Ein Datenstrom kann gepuffert werden. Diese beeinflusst Zuverlässigkeit und Bandbreite nicht. Es erhöht die Verzögerung bietet aber die Möglichkeit Jitter zu vermindern. Traffic Shaping. Traffic Shaping kann als rate basierte Flusskontrolle betrachtet werden. Bei Verfahren wie Sliding Window wird Menge an Daten die verschickt wird kontrolliert. Beim Traffic Shaping kontrolliert man die Datenübertragungsrate. Die Idee beim Traffic Shaping ist, dass der Sender beim Verbindungsaufbau einen Vertrag eingeht, der ihm eine gewisse Datenrate zusichert, wenn er eine gewissen Senderate nicht überschreitet. Zur Realisierung von Traffic Shaping werden Leaky Bucket- oder Token-Bucket-Algorithmen eingesetzt. Wichtig ist, das beim Buffering auf Empfängerseite gepuffert wird, beim Traffic Shaping auf Senderseite. Resource Reservation. Das Abschliessen eines Traffic Contracts erlaubt es den an der Verbindungen teilnehmenden Router entsprechend Ressourcen zu reservieren. Ressourcen sind Bandbreite, Puffer und Rechenzeit. Admission Control. Wenn ein Router erkennt, dass es die im Vertrag geforderten Anforderungen nicht erfüllen kann, darf er die Verbindung nicht annehmen. 8.3.3 Wie garantiert man eine niedrige Auslastung? Um eine niedrige Auslastung garantieren zu können, dürfen die Anforderung in den angenommenen Traffic Contracts die Ressourcen nicht überschreiten. 8.3.4 Was ist Jitter? Jitter ist die Standardabweichung der Verzögerung, also die durchschnittliche Abweichung von der durchschnittlichen Verzögerung. Video und speziell Audio ist sehr anfällig für Audio. Da das menschliche Ohr Schwankungen sofort raus hört. 8.3.5 Was ist ein isochroner Dienst? Bei isochronen Diensten sollten Sender und Empfänger möglichst gleich laufen (also kein Jitter). 8.3.6 Welche Dienstklassen bietet ATM? Constant Bit Rate (CBR). Durch die konstante Bitrate eignet sich die CBR-Klasse um eine Kupferoder Glasfaserleitung zu emulieren. Damit kann z.B. ein Sprachkanal realisiert werden, der sich wie ein herkömmlicher ISDN B-Kanal verhält. Ausserdem kann die CBR-Klasse zur EchtzeitÜbertragung unkomprimierter Video/Audio-Daten verwendet werden. Realtime Variable Bit Rate (RT-VBR). Bei den meisten Komprimierungsverfahren für Audio und Video (z.B. MPEG) entsteht eine Datenstrom mit unterschiedlichen Bitraten. Um Echzeitanwendungen zu ermöglichen wird in dieser Klasse garantiert, dass kein Jitter auftritt. Der gelegentliche Bit- oder Zellverlust ist im Gegensatz zum Jitter zu verschmerzen. Non Realtime Variable Bit Rate (nRT-VBR). Diese Klasse eignet sich ebenfalls für Anwendungen mit variierender Bitrate, gibt aber keine Jitter-Garantie. Unspecified Bit Rate (UBR). Die UBR-Klasse mach keinerlei Garantien über die Bitrate und warnt auch nicht vor Überlastungen. Dieses Verfahren ähnelt dem von IP und eignet sich daher auch gut für die Übertragung von IP-Paketen. Available Bit Rate (ABR). Die ABR-Klasse bietet die Möglichkeit eine Verbindung zwischen A und B zu beantragen, die immer mindestens 5 MBit/s leistet, aber nach Möglichkeit des Netzes auch mehr zur Verfügung stellt. So kann man z.B. angeben, dass Spitzen von 10 MBit/s auftreten können. Die 5 MBit/s sind dann garantiert und die 10 MBit/s werden nach Möglichkeit zur Verfügung gestellt. Diese Klasse würde sich z.B. für Web-Surfen eignen. 8.3.7 Welche Möglichkeiten gibt es QoS über IP zu realisieren? Für QoS in IP-Netzwerken gibt es zwei verschieden Ansätze 98 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 8.3. Dienstgüte Integrated Services. Hier ist die Idee ähnlich wie bei ATM eine Verbindung mit zugesicherten Eigenschaften aufzubauen. Dazu müssen auf jedem einzelnen Router die Ressourcen reserviert werden. Das dabei verwendete Protokoll heisst Resource Reservation Setup Protocol (RSVP). Das Problem ist genau, dass jeder Router dieses Protokoll unterstützen muss. Ausserdem ist die Skalierbarkeit eher begrenzt, weil für jede Verbindung ein RVSP-Tunnel aufgebaut werden muss. Differentiated Services. Bei diesem Ansatz werden keine Verbindungen aufgebaut, sondern die Pakete in verschiedene Prioritätsklassen eingeteilt und dann entsprechend ihrer Priorität geroutet. Dies ist insofern einfach zu realisieren, da auch der IPv4 Header ein Type Of Service Feld enthält. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 99 Literaturverzeichnis [Duden Informatik, 1993] Duden Informatik. 2. Aufl. Dudenverlag, 1993 [Hennesy & Patterson, 2003] Hennesy, John L.; Patterson, David A. Computer Architecture: A Quantitative. Approach. 3. Aufl. Morgan Kaufman, 2003 [Jessen, 2001] Jessen E. Skript zur Vorlesung Rechnerarchitektur WS 00/01. [Rechenberg & Pomberger, 1999] Rechenberg, Peter; Pomberger, Gustav (hrsg.) Informatik-Handbuch. 2. Aufl. Hanser, 1999 [Tanenbaum, 1997] Tanenbaum, Andrew S. Computernetzwerke. 3. Aufl. Prentice Hall, 1997 [Tanenbaum, 2001] Tanenbaum, Andrew S. Modern Operating Systems. 2. Aufl. Prentice Hall, 2001 [Tanenbaum, 2003] Tanenbaum, Andrew S. Computer Networks. 4. Aufl. Prentice Hall, 2003 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 101 Teil III Theoretische Informatik DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 103 Kapitel 9 Effiziente Algorithmen und Datenstrukturen 9.1 Allgemeines 9.1.1 Was ist dynamisches Programmieren? Wie Divide&Conquer wird beim dynamischen Programmieren ein Problem in Subproblem aufgeteilt, diese gelöst und die Gesamtlösung aus den Teillösungen zusammengesetzt. Dynamisches Programmieren verwendet man, wenn die Subproblem (im Gegensatz zu Divide&Conquer) nicht unabhängig sind. Zwischenergebnisse werden in Tabellen gespeichert. Dynamisches Programmieren kommt meist bei Optimierungsproblemen zum Einsatz. 9.1.2 Ein Beispiel für dynamische Programmierung? Knapsack, Floyd-Warshall, CYK, LCS 9.1.3 Wie können Graphen repräsentiert werden? Mit Adjazenzenlisten oder Adjazenzenmatrizen. 9.1.4 i Was versteht man unter amortisierter Kostenrechnung? Mit der amortisierten Kostenrechnung gibt man die durchschnittlichen Kosten von Operationen in Folgen an. Angegeben wird der Worst-Case aller Folgen. Eine Möglichkeit die amortisierten Kosten zu bestimmen ist die Potentialmethode, die wie folgt funktioniert. Man startet mit einer Datenstruktur D0 , auf der n Operationen ausgeführt werden. Für jedes i = 1, 2, . . . , n gibt ci die tatsächlichen Kosten der i-ten Operation an. Di ist die Datenstruktur, die durch Anwendung der i-ten Operation auf die Datenstruktur Di−1 entsteht. Eine Potentialfunktion Φ ordnet jeder Datenstruktur Di ein reelle Zahl Φ(Di ) zu. Diese Zahl ist das Potential der Datenstruktur Di . Die amortisierten Kosten ĉi der i-ten Operation unter Berücksichtigung der Potentialfunktion Φ ist definiert durch ĉi = ci + Φ(Di ) − Φ(Di−1 ). Die amortisierten Kosten einer Operation sind also die tatsächlichen Kosten plus die Potentialzunahme. Wenn die Potentialzunahme bei Operation i positiv ist, wird sozusagen überbezahlt, das Potential der Datenstruktur erhöht sich. Ist sie negativ, wird angespartes Potential aufgebraucht. Für die amortisierten Kosten von n Operationen gilt somit (der zweite Schritt ergibt sich wegen der DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 105 Kapitel 9. Effiziente Algorithmen und Datenstrukturen Teleskopsumme) n X ĉi = i=1 = n X i=1 n X i=1 (ci + Φ(Di ) − Φ(Di−1 )) ci + Φ(Dn ) − Φ(D0 ) Wichtig ist, dass gelten muss Φ(Dn ) − Φ(D0 ) ≥ 0, sonst sind die amortisierten Kosten keine obere Schranke der tatsächlichen Kosten. Da man in vielen Fällen die Anzahl n der Operationen zu Beginn nicht weiss, muss man dafür sorgen, dass für alle i Φ(Di ) ≥ Φ(D0 ) gilt. Oft ist es praktisch, Φ(D0 ) = 0 zu setzen und für jedes i > 0 zu zeigen, dass Φ(Di ) ≥ 0 gilt. 9.1.5 Was ist Divide&Conquer? Bei Divide&Conquer wird ein Problem in Subprobleme zerlegt, diese werden (rekursiv) gelöst und die Gesamtlösung aus den Teillösungen zusammengesetzt. 9.1.6 Welche Aussagen können Sie über die Laufzeit von Divide&Conquer-Algorithmen machen? Wenn man die Rekursionsgleichung für einen Divide&Conquer-Algorithmus kennt, kann in vielen Fällen mit Hilfe des Master Theorems die Laufzeit bestimmt werden. Master Theorem: Seien α ≥ 1 und β ≥ 1 zwei Konstanten und sei f (n) eine nicht negative Funktion und sei T (n) definiert durch die Rekursionsgleichung n + f (n) T (n) = αT β l m j k wobei nβ sowohl als nβ als auch als nβ gelesen werden kann, dann ist Θ nlogβ α , wenn f (n) = O nlogβ α− für ein konstantes > 0, Θ nlogβ α log n , wenn f (n) = Θ nlogβ α , logβ α+ T (n) = Θ (f (n)) , wenn für ein konstantes > 0 und f(n) = Ω n αf nβ ≤ cf (n) für ein konstantes 0 < c < 1. Man erkennt das die Laufzeit von Divide&Conquer-Verfahren hauptsächlich von der Funktion f , die für das Zusammensetzen der Gesamtlösung aus den Teillösungen zuständig ist, abhängt. 9.1.7 Erklären Sie den Unterschied zwischen Worst-Case-, Average-Case- und amortisierten Kosten. Worst-Case-Kosten. Die Worst-Case-Kosten geben eine obere Schranke für die Laufzeit an, gelten also unabhängig von der Art der Eingabedaten. Average-Case-Kosten. Die Average-Case-Kosten geben die Kosten für einen durchschnittlichen Ablauf eines Algorithmus an. Dies macht aber noch eine Aussage über das Worst-CaseVerhalten. Ein Beispiel ist Quicksort, dass Average-Case-Kosten von O(n log n) hat, im schlechtesten Fall aber quadratische Kosten hat. Amortisierte Kosten. Bei bestimmten Datenstrukturen wie Fibonacci-Heaps macht es keinen Sinn alle Operationen getrennt zu betrachten, da die Operationen dort sehr unterschiedlich viel Arbeit verrichten. Deshalb werden dort Folgen von Operationen auf eine Datenmenge betrachtet. Angegeben werden dann die durchschnittlichen Kosten pro Operation für den Worst-Case aller Folgen. 9.1.8 Was sind Graphen? Ein Graph ist ein Tupel (V, E), wobei V eine endliche Menge von Knoten ist und E ⊆ von Kanten ist. 106 V 2 eine Menge http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.2. Mathematische Grundlagen 9.1.9 Was ist die transitive Hülle? Die transitive Hülle eines gerichteten Graphen G = (V, E) ist der Graph G∗ = (V, E ∗ ) wobei E ∗ = {(i, j) | es gibt einen Pfad von Knoten j nach i in G} ist. 9.1.10 Was ist das uniforme und was das logarithmische Kostenmaß? Beim uniformen Kostenmaß wird die Länge (in Bits) der einzelnen Operatoren nicht beachtet, beim logarithmischen Kostenmaß schon. Das logarithmische Kostenmaßmuss verwendet werden, wenn die Operator sehr gross sind (im Vergleich zur normalen Registergrösse). Beispiel: Der Algorithmus, der testet ob eine Zahl n eine Primzahl √ ist hat im uniformen Kostenmaß √ die Komplexität O( n), da er einfach für alle Zahlen {2, 3, . . . , n} testet ob sie die Zahl n teilen. Nun das logarithmische Kostenmaß. Wenn die Zahl n binär√kodiert wird, beträgt die Eingabelänge l = log2 n. Als Laufzeit erhält man für den Algorithmus O( 2l ) was aber leider O(2l/2 ) entspricht. Dieser Algorithmus hat also exponentielle Laufzeit und ist daher für grosse n unbrauchbar. 9.1.11 Was ist das allgemeine Prinzip das im CYK-Algorithmus verwendet wird? Dynamische Programmierung. 9.1.12 Welche algorithmische Prinzipien kennen Sie (mit Beispielen)? Greedy: Kruskal, Prim Divide&Conquer: Mergesort, Quicksort Dynamische Programmierung: Knapsack, Floyd-Warshall, CYK, LCS 9.2 Mathematische Grundlagen 9.2.1 i Sie haben in der Vorlesung Rekursionsgleichungen kennen gelernt. Erzählen Sie etwas dazu. Da die Laufzeit von vielen Algorithmen mit einer Rekursionsgleichung beschrieben werden kann, interessiert man sich für die geschlossene Form solcher Rekursionsgleichungen. Zur Bestimmung dieser Form existieren verschiedene Verfahren. Formeln. Für einfache Rekursionsgleichung, d. h. homogene und inhomogene Rekursionsgleichung ersten Grades sowie homogene Rekursionsgleichung zweiten Grades existieren Lösungsformeln. Siehe dazu [DS-Faq, 2001]. Substitutionsmethode. Die Idee bei der Substitutionsmethode ist, eine obere Grenze für die Rekursionsgleichung zu raten und diese durch Nachrechnen zu beweisen. Dies wird z. B. beim der Laufzeitanalyse von BFPRT gemacht (9.4.14). Iterationsmethode. Die Idee bei der Iterationsmethode ist, die Rekursion immer wieder zu expandieren und somit eine Form zu erreichen, die nur von n und den Startbedingungen abhängt. Der Vorteil ist, dass man keine obere Grenze erraten muss, dafür erfordert sie einiges an Rechenarbeit. Manchmal kann man ohne die Iterationsmethode zu Ende zu führen das Ergebnis erraten und dann die Substitutionsmethode anwenden. Master-Theorem. Die Laufzeit von Divide&Conquer-Algorithmen lässt sich oft mit dem MasterTheorem bestimmen, siehe dazu 9.1.6. Erzeugendenfunktionen. Ein sehr mächtiges (aber auch recht komplexes) mittel zum Lösen von Rekursionsgleichungen sind Erzeugendenfunktionen, siehe dazu [DS-Faq, 2001] und [Graham et. al., 1989]. 9.2.2 i Wie kann man eine Rekursionsgleichung mit Hilfe einer Erzeugendenfunktion lösen? Das ist in [DS-Faq, 2001] und [Graham et. al., 1989] ausführlich beschrieben. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 107 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 9.3 Datenstrukturen Hinweis: Wenn nicht anders angegeben gilt im Folgenden n = |V | und m = |E|. 9.3.1 Was sind Fibonacci-Heaps und wieso heissen sie so? Der Fibonacci-Heap ist eine Heap-Implementierung bei der alle Operationen bis auf Extract-Min in O(1) ablaufen, Extract-Min benötigt O(log n). Die Grundidee der Fibonacci-Heaps ist, jegliche Neuordnung des Heaps solange wie möglich hinauszuzögern. Nur bei Extract-Min wird der Heap neu geordnet. Genau wie der Binomial-Heap besteht der Fibonacci-Heap aus mehreren Bäumen, die an den Wurzel verbunden sind. Ausserdem gibt es einen Zeiger min[H] der auf das kleinste Element der Wurzelliste und somit auf das kleinste Element überhaupt zeigt. Insert (O(1)): Es wird ein neuer Baum mit einem einzigen Knoten erzeugt, dieser wird an die Wurzelliste angehängt. Fall nötig wird min[H] umgesetzt. Minimum (O(1)): Der Zeiger min[H] zeigt immer auf das kleinste Element. Merge (O(1)): Die Wurzellisten der beiden Heaps H1 und H2 werden konkateniert. der Zeiger min[H] wird auf min(min[H1 ], min[H2 ]) gesetzt. Extract-Min (O(log n)): Bei dieser Operation wird die Neuordnung des Heaps durchgeführt und zwar so, dass danach alle Wurzel der Wurzelliste unterschiedlichen Grad haben (der Grad gibt die Anzahl der Söhne eines Knotens an). Es lässt sich zeigen, dass die amortisierten Kosten für das Löschen des Minimums O(D(n)) betragen, wobei D(n) der maximale Grad eines Knotens einem Heap mit n Knoten ist. Es gilt folgendes Lemma (size(x) bezeichnet die Anzahl der Knoten im Unterbaum von x, x mitgezählt): Lemma Für jeden Knoten x vom Grad k eines Fibonacci-Heaps gilt size(x) ≥ Fk+2 (daher der Name Fibonacci-Heaps). Mit diesem Lemma lässt sich nun Zeigen, dass D(n) = O(log n): Sei x einen Knoten mit Grad k in einem n-Knoten Fibonacci-Heap. Laut Lemma gilt n ≥ √ size(x) ≥ Fk+2 . Weiterhin gilt, dass Fk+2 ≥ φk , wobei φ der goldene Schnitt ist, φ = (1 + 5)/2. Somit gilt n ≥ size(x) ≥ φk . Logarithmieren ergibt k ≤ logφ n. Also ist D(n) = O(log n). Trotz der guten Laufzeiten werden Fibonacci-Heaps wegen der aufwendigen Implementierung in der Praxis nicht sehr häufig eingesetzt. 9.3.2 Was ist ein Binary Heap? Ein Binary Heap ist ein Binärbaum in dem für jeden Knoten gilt, dass sein Schlüssel grösser ist als die Schlüssel aller Kinder. 1 16 2 3 14 10 1 8 2 4 5 6 7 8 7 9 3 9 4 2 3 4 5 6 7 8 9 10 16 14 10 8 7 9 3 2 4 1 10 1 Wie die Abbildung zeigt, werden Binary Heaps eigentlich immer als Arrays implementiert. Damit kann sehr einfach auf den Vater sowie die rechten und linken Kinder zugegriffen werden: Parent(i) Left(i) Right(i) 108 : bi/2c : 2i : 2i + 1 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen Mit Heaps kann sowohl der Heapsort Algorithmus als auch eine Priority Queue realisiert werden. Laufzeiten: Insert Extract-Max Build-Heap Heapify Heapsort 9.3.3 O(log n) O(log n) O(n) O(log n) O(n log n) stellt Heap-Bedingung wieder her Was ist ein Binomial-Baum Der Binomial-Baum Bk ist ein geordneter Baum, der rekursiv definiert ist. der B0 hat nur einen einzigen Knoten. der Bk besteht aus den beiden Binomial-Bäumen Bk−1 die wie folgt miteinander verbunden sind: die Wurzel des einen Baumes ist das linkeste Kind der Wurzel des anderen Baumes Der Baum Bk hat folgenden Eigenschaften: 1. es gibt 2k Knoten 2. die Höhe des Baumes ist k 3. auf der Tiefe i gibt es ki Knoten, daher der Name 4. die Wurzel hat Grad k, das ist der höchste vorkommende Grad im Baum 9.3.4 Was sind Binomial-Heaps und wieso heissen sie so? Ein Binomial-Heap H ist eine Menge von Binomial-Bäumen die folgenden Bedingungen erfüllt. 1. jeder Binomial-Baum in H erfüllt die Heap-Bedingung so dass für jeden Knoten gilt, dass sein Schlüssel mindestens so gross wie der seines Vater ist 2. jeder Bi kommt nur einmal in H vor Diese Binomial-Bäume werden an den Wurzel durch eine Liste verbunden. Die zweite Bedingung besagt, dass der Binomial-Heap H mit n Knoten höchstens blog nc + 1 viele Binomial-Bäume und damit auch höchstens blog nc + 1 Wurzel in der Kette hat. Damit dauert die Suche nach dem kleinsten Element O(log n). Laufzeiten: Make-Heap Insert Minimum Extract-Min Union Decrease-Key Delete 9.3.5 O(1) O(log n) O(log n) O(log n) O(log n) O(log n) O(log n) Was sind Suchbäume? Suchbäume sind Datenstrukturen, die folgende Operationen unterstützen: Search Minimum Maximum Predecessor Successor Insert DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 109 Kapitel 9. Effiziente Algorithmen und Datenstrukturen Delete Suchbäume können für Dictionaries und Priority Queues eingesetzt werden. 9.3.6 Was sind Rot-Schwarz-Bäume? Rot-Schwarz-Bäume sind externe Binärbäume mit roten und schwarzen Kanten, sodass gilt 1. Alle Blätter hängen an schwarzen Kanten. 2. Alle Blätter haben die gleiche Schwarztiefe. 3. Kein Pfad von der Wurzel zu einem Blatt enthält aufeinander folgende rote Kanten. Die Schwarztiefe eine Knotens ist die Anzahl der schwarzen Kanten auf dem Pfad von der Wurzel zum Knoten. Ein Rot-Schwarz-Baum hat die Tiefe O(log n), das führt zu den folgende Laufzeiten der Dictionary-Operationen. Search. Die Suche funktioniert wie immer in einem Suchbaum. Laufzeit ist O(log n). Insert. Wie bei den (a, b)-Bäumen wird zuerst Search durchgeführt um das Blatt w zu finden, wo der einzufügende Schlüssel noch nicht hängt. Das Blatt w wird dann durch einen Knoten w 0 mit Kinder w und v ersetzt. Wobei v den neuen Schlüssel hat. Wenn danach zwei aufeinander folgende rote Kanten an w 0 liegen, wird durch Rotation oder Doppelrotation wieder die Ordnung hergestellt. Laufzeit ist O(log n). Delete. Beim Löschen eines Schlüssels muss der Vater des Blattes durch den Bruder des Blattes ersetzt werden um die Binärbaumstruktur aufrecht zu halten. Danach steht u. U. eine Reorganisation des ganzen Baumes verändert werden. Trotzdem ist die Laufzeit O(log n). Rot-Schwarz-Bäume können zur Implementierung von (2, 4)-Bäumen dienen. 9.3.7 Was ist eine Priority Queue? Priority Queue ist eine Datenstruktur, die folgende Operationen unterstützt: Insert Minimum Extract-Min (Decrease-Key) 9.3.8 Wo werden PQs verwendet? Bei den Algorithmen von Prim und Dijkstra. Kruskal kann auch mit PQs implementiert werden. 9.3.9 i Wie kann man PQs implementieren? Es gibt verschiedene Möglichkeiten zur Implementierung von PQs, die sich in den Laufzeiten und des Aufwands unterscheiden. Binary-Heaps Binomial-Heaps Fibonacci-Heaps 1-Level und 2-Level Buckets Radix Heaps 9.3.10 Was sind Union-Find-Strukturen? Eine Union-Find-Struktur erlaubt es Mengenoperationen effizient zu implementieren, sie stellt folgende Operationen zur Verfügung. 110 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen Make-Set Union Find-Set Die beste bekannte Implementierung von Union-Find-Strukturen funktioniert mit Bäumen, wobei die Wurzel jeweils eines Baumes jeweils den Repräsentanten darstellt. Wenn noch die Heuristiken Union by Rank und Path Compression angewandt werden, kann für m Operation auf n Element die Laufzeit (mα(m, n)) erreichen. Wobei α die inverse Ackermannfunktion ist. 9.3.11 Erklären Sie die Union-Operation. Wenn die Union-Find-Struktur als Wald implementiert ist, wird die Wurzel des kleineren Teilbaums an die Wurzel des grösseren Teilbaums gehängt. Man nennt dieses Vorgehen Union by Rank und verwaltet den Rang in einem Array rank []. 9.3.12 Was ist Pfadkompression? Bei Find-Set werden alle Knoten direkt an die Wurzel gehängt, dadurch laufen folgende Find-SetOperationen schneller ab. 9.3.13 Welche Komplexität hat die Find -Operation bei Union-Find-Strukturen mit Path-Compression? Wenn bereits vorher einmal nach einem Element der Menge gesucht wurde, ist die Komplexität O(1), da alle Knoten direkt an der Wurzel hängen. Im schlimmsten Fall kann sie O(n) wobei n die Anzahl der Knoten in diesem Teilbaum ist. 9.3.14 Was bedeutet log∗ n? Dazu muss zuerst die Funktion log(i) n definiert werden. wenn i = 0 n (i−1) (i) log(log n) wenn i > 0 und log(i−1) n > 0 log n = undefiniert wenn i > 0 und log(i−1) n ≤ 0 oder log(i−1) n undefiniert log∗ n ist dann wie folgt definiert. log∗ n = min{i ≥ 0 | log(i) n ≤ 1} Damit ist log∗ n genau die Umkehrfunktion zur wiederholten Potenzierung. 9.3.15 Was ist eine Rotation in einem Suchbaum? Durch eine Rotation in einem Suchbaum kann ein Knoten um ein Niveau angehoben oder abgesenkt werden ohne das die Suchbaumeigenschaft verletzt wird. Der Begriff Rotation ist eigentlich irreführend. Falls man sich wirklich eine Rotation vorstellen will, liegt das Rotationszentrum anschaulich eher auf der Kante als am Knoten. Sonst würde ja nur der eine Knoten verändert. Die Abbildung zeigt Linksund Rechtsrotation. RIGHT-ROTATE(y) y α 9.3.16 α γ x β x LEFT-ROTATE(x) y β γ i Was sind Splay-Trees? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 111 Kapitel 9. Effiziente Algorithmen und Datenstrukturen Splay-Trees stellen eine selbst-organisierende Datenstruktur dar. Es handelt sich um binäre Suchbäume, die aber im Gegensatz zu balancierten Bäumen keinen weiteren Bedingung unterliegen. Beim Zugriff auf ein Element x wird eine Variante der Move-To-Root-Heuristik angewandt, die das Element an die Wurzel des Baumes verschiebt und den Baum verbreitert. Die Länge aller Suchpfade zu Elementen auf dem Suchpfad von x halbiert sich in etwa. Die Operation Splay(t, x) führt die Reorganisation für Baum t und Element x durch. Die Wörterbuch-Operationen laufen wie folgt ab: Search: Zuerst wird Splay(t, x) durchgeführt. Ist x enthalten steht es danach an der Wurzel, ansonsten ist es nicht enthalten. Insert: Um Element x in Baum t einzufügen, wird zuerst Splay(t, x) durchgeführt. Dadurch können sich drei Fälle ergeben: 1. x steht an der Wurzel. In diesem Fall gibt es nichts zu tun, da das Element bereits vorhanden ist. 2. x steht nicht an der Wurzel und x ist nicht kleiner als alle Elemente in t. In diesem Fall macht man x zur Wurzel und hängt die Teilbäume der alten Wurzel entsprechend um. 3. x steht nicht an der Wurzel und x ist kleiner als alle Elemente in t. Auch in diesem Fall macht man x zur Wurzel, allerdings ist danach der linke Unterbaum von x leer. Delete: Um Element x aus t zu löschen wird zuerst Splay(t, x) durchgeführt. Falls x danach nicht an der Wurzel steht, ist nichts zu tun, da x nicht im Baum enthalten ist. Andernfalls steht x an der Wurzel und hatte den linken Unterbaum tl und den rechten Unterbaum tr . Jetzt wird x gelöscht, danach führt man Splay(tl , ∞) aus, wobei ∞ eine Element ist, dass grösser ist als alle Elemente in tl . Dadurch erhält man einem Baum t0l , der den grössten Schlüssel y von tl an der Wurzel und eine leeren rechten Teilbaum hat. Diesen Teilbaum ersetzt man jetzt durch t r und hat wieder eine gültigen Baum. Mit einer amortisierten Kostenanalyse kann man Zeigen, dass m Splay-Tree-Operation auf n Elementen O(m log n) Zeit brauchen, daher ergeben sich im Durchschnitt Kosten von O(log n) pro Operation. 9.3.17 Wie funktioniert die Splay-Operation? Die Aufgabe der Splay(t, x)-Operation ist es, dass Element x an die Wurzel des Baumes zu verschieben und den Baum breiter zu machen. Im Folgenden sei v der Knoten bei dem das Element gefunden wurde. Falls x nicht gefunden wird, sei v der Vater des Blattes bei dem die Suche erfolglos endet. Der Vater von v heisst p[v], der Grossvater von v ist g[v] = p[p[v]]. Die Splay(t, x)-Operation läuft nun in zwei Schritten ab: 1. Zuerst wird der Knoten v gesucht. 2. Jetzt werden die Operationen Zig, Zig-Zig und Zig-Zag durchgeführt bis sie nicht mehr ausgeführt werden können, weil v Wurzel des Baumes ist. Die Operationen funktionieren so: Zig: Diese Operation wird durchgeführt falls p[v] die Wurzel des Baumes ist. Es wird eine Rotation nach links oder rechts durchgeführt, so dass v neue Wurzel des Baumes wird. v p[v] γ v α β RR(p[v]) α p[v] β γ Zig-Zig: Diese Operation wird durchgeführt falls v einen Vater p[v] und einen Grossvater g[v] hat und sowohl v als auch p[v] rechte (linke) Söhne ihres jeweiligen Vaters sind. Es werden zwei aufeinander folgende Rotationen in die gleiche Richtung durchgeführt. Zuerst bei g[v] und dann bei p[v], dadurch wird v um zwei Niveaus angehoben. 112 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen v g[v] p[v] δ p[v] RR(g[v]) γ v α RR(p[v]) v α p[v] g[v] β α β βγ δ g[v] γ δ Zig-Zag: Diese Operation wird durchgeführt falls v einen Vater p[v] und einen Grossvater g[v] hat und einer der beiden Knoten v und p[v] linker und der andere rechter Sohn seines jeweiligen Vater ist (oder umgekehrt). Jetzt werden zwei Rotationen in gegengesetzter Richtung durchgeführt. Die erste bei p[v], die zweite bei g[v], dadurch wird v um zwei Niveaus angehoben. g[v] g[v] v α p[v] v δ RR(p[v]) α LR(g[v]) v g[v] β γ β p[v] p[v] γ α βγ δ δ Der einzige Unterschied zur normalen Move-to-Root-Heuristik liegt in der Operation Zig-Zig. Hier wird nicht wie bei Move-To-Root streng von unten nach oben vorgegangen, die Rotation wird zuerst für den Grossvater und dann für den Vater durchgeführt. 9.3.18 i Was ist Hashing? Hashing ist eine Methode mit der man die Wörterbuchoperation Insert, Delete und Search in einigen Fällen mit der durchschnittlichen Laufzeit O(1) realisieren kann. Es wird eine Hashfunktion definiert, die jedem Element k des Universums U einen Slot h(k) zuweist. Im folgenden gilt folgendes: U ist das Universum, N = |U | K ⊂ U ist die Schlüsselmenge, n = |K| Array T [0 . . . m − 1] ist die Hashtabelle Hashfunktion h : U → [0 . . . m − 1] ki sind Schlüssel h(ki ) sind Slots 9.3.19 Welche Probleme gibt es beim Hashing und wie werden die gelöst? Probleme die beim Hashing auftreten können sind Kollisionen. Es kann passieren, das zwei (oder mehrere) Elemente k1 und k2 auf den selben Slot h(k) gehasht werden. Zur Behandlung solcher Kollisionen gibt es mehrere Methoden: 1. Chaining Alle Elementen die auf den selben Slot h(k) gehasht werden, werden in eine verkettete Liste gehängt, deren Kopf in h(k) liegt. Beim Chaining ergeben sich folgende Laufzeiten: Insert: O(1) Delete: O(1) wenn die Listen doppelt verkettet sind, wenn nicht entspricht sie der von Search Search: die Worst-Case-Laufzeit ist O(n), wenn alle Elemente im selben Slot landen. Wenn man annimmt, dass die Hasfunktion die Element auf alle Slots mit gleicher Wahrscheinlichkeit verteilt ist die Average-Case-Laufzeit von Search O(1 + α), wobei α der load factor, das Verhältnis der Elemente in der Tabelle zur Anzahl der Slots, ist. Dies gilt für erfolgreiche und erfolglose Suche. Wenn die Anzahl der Slots proportional zur Anzahl der Elemente ist, führt das zur Laufzeit O(1). 2. Open addressing Beim Hashing mit offener Adressierung werden alle Element in der Tabelle selbst abgelegt. Wird beim Insert oder Search des Schlüssels k ein Element 6= k im Slot h(k) gefunden, so wird auf DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 113 Kapitel 9. Effiziente Algorithmen und Datenstrukturen deterministische Weise eine alternative Adresse berechnet. Für jeden Schlüssel wird also eine Sondierungsfolge festgelegt. Das heisst die Hashfunktion bekommt einen zweiten Eingabeparameter (die probe number ) der beim Insert inkrementiert wird, bis ein freier Slot gefunden wurde. Bei Search wird ebenfalls inkrementiert bis der Schlüssel gefunden wurde oder ein leerer Slot entdeckt wurde, dann endet die Suche erfolglos. Ein Problem ergibt sich beim Delete. Wenn man den Schlüssel einfach löschen würde, zerbräche man die Sondierungsreihenfolge und könnte die dahinter liegenden Elemente nie mehr finden. Deshalb löscht man nicht, sonder markiert den Slot als gelöscht . Dadurch sind die Laufzeiten aber nicht mehr von α abhängig. Aus diesem Grund wird normalerweise Chaining verwendet, wenn auch gelöscht werden soll. Für das Sondieren gibt es mehrere Vorgehensweisen. Generell gelten im folgenden diese Definitionen. h(k) ist ursprüngliche Hashfunktion, s(i, k) ist eine Sondierungsfunktion, die die Probe number und einen Schlüssel nimmt und h(i, k) = (h(k) − s(i, k)) mod m. Man startet dann immer mit h(k) und versucht, falls diese belegt ist, als nächstes h(1, k) = h(k) − s(1, k). a) Linear Probing Beim linearen Sondieren ist die Sondierungsfunktion wie folgt definiert: s(i, k) = j ⇒ h(i, k) = (h(k) − j) mod m D. h. man probiert zuerst T [h(k)], dann T [h(k)−1], dann T [h(k)−2], usw. Lineares sondieren ist sehr einfach zu implementieren hat aber das Problem des Primary Clustering. Um die Elemente herum, die Kollisionen verursachen, bilden sich Häufungen. Für die durchschnittliche Anzahl der Sondierungsschritte gilt: 1 1+ 1 erfolgreich 2 1−α E [# probes] = 1 1 erfolglos 2 1 + (1−α)2 b) Quadratic Probing Beim quadratischen Sondieren ist die Sondierungsfunktion wie folgt definiert: s(i, k) = (−1)j 2 j 2 D. h. man probiert T [h(k)], T [h(k) + 1], T [h(k) − 1], T [h(k) + 4], T [h(k) − 4], . . . Man muss hier aufpassen, dass das auch tatsächlich eine Permutation über alle Slots ergibt. Es lässt sich zeigen, dass dies für bestimmte prime m so ist. Das Clustering tritt hier viel weniger stark auf und heisst Secondary Clustering. Für die durchschnittliche Anzahl der Sondierungsschritte gilt: 1 + ln 1 erfolgreich − α2 1−α E [# probes] = 1 1 erfolglos 1−α − α + ln 1−α c) Double Hashing Beim doppelten Hashing ist die Sondierungsfunktion wie folgt definiert: s(i, k) = jh0 (k) wobei h0 eine zweite Hashfunktion ist. Damit h(i, k) = (h(k) − s(i, k)) mod m eine Permutation aller Hashadressen ergibt, muss h0 (k) für alle Schlüssel relativ prim zu m sein. Das kann man z. B. erreichen indem man als m eine Zweierpotenz wählt und h0 so definiert, dass sie nur ungerade Zahlen erzeugt. Für die durchschnittliche Anzahl der Sondierungsschritte gilt: E [# probes] = 9.3.20 114 1 1 α m 1−α 1 1−α erfolgreich erfolglos i Was ist universelles Hashing? http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen Beim normalen Hashing kann es immer passieren, dass die Hashfunktion alle Schlüssel einer bestimmten Eingabe in den selben Slot hasht und damit Worst-Case-Laufzeit produziert. Dies kann vermieden werden, in dem die Hashfunktion zufällig aus einer Menge von Hashfunktionen gewählt wird. Sei H eine solche Menge von Hashfunktionen. Die Menge h heisst universell wenn für jedes Paar unterschiedlicher Schlüssel x, y ∈ U , die Anzahl der Hashfunktionen für die h(x) = h(y) gilt, genau |H|/m ist. Das bedeutet, wenn man eine Hashfunktion h zufällig aus H auswählt ist die Wahrscheinlichkeit einer Kollision zwischen zwei Schlüsseln x, y gleich 1/m. Dies entspricht genau der Wahrscheinlichkeit eine Kollision zu erzeugen, wenn man die beiden Slots vollkommen zufällig bestimmen würde, also den Annahmen des uniformen Hashings. 9.3.21 i Was ist perfektes Hashing? Das folgende gilt nur für das statische Dictionary-Problem, wobei nur Search-Operation zugelassen ist. Eine Hashfunktion ist perfekt, wenn Sie alle Schlüssel auf unterschiedliche Slots abbildet. Eine minimale perfekt Hashfunktion braucht nur den Platz m = |K| = n. Für m ≥ 3n kann man in Zeit O(nN ) eine perfekte Hashfunktion für K ⊂ U finden, die in O(1) auszuwerten ist (durch ein Programm der Grösse O(n log N ) Bits). 9.3.22 Welche Bäume setzt man in einer Datenbank ein? B-Bäume. 9.3.23 9.3.24 Geben Sie eine grobe Klassifizierung von Datenstrukturen an. Klasse Operationen Implementierung Dictionary Priority Queue Mergeable Heaps Concatenable Queues Search, Insert, Delete Minimum, Insert, Extract-Min, (Search) Minimum, Insert, Delete, Merge Minimum, Insert, Delete, Merge Hashtabelle, balancierte Bäume Heaps 2-3-Bäume, Binomial-Wälder 2-3-Bäume Was sind (a, b)-Bäume? (a, b)-Bäume sind Suchbäume mit den folgenden Eigenschaften. Alle Blätter haben die gleiche Tiefe. Die Schlüssel sind nur in den Blättern gespeichert (externer Suchbaum). Für alle Knoten v ausser der Wurzel gilt: a ≤ #Kinder ≤ b. Für die Wurzel gilt: 2 ≤ #Kinder ≤ b. Es gilt b ≥ 2a − 1 Für alle inneren Knoten v gilt: Hat v l Kindern, so sind in v l − 1 Werte k1 . . . kl−1 gespeichert und es gilt: ki−1 < key(w) ≤ ki für alle Knoten w im i-ten Unterbaum von v, wobei k0 = −∞, kl = +∞ Die Wörterbuchoperationen funktionieren wie folgt. Search (k). Der Algorithmus zum finden eines Elementes startet an der Wurzel und läuft entsprechend der Informationen, die in den Knoten gespeichert sind, nach unten bis zu einem Blatt. Dort findet er entweder den gesuchten Schlüssel oder nicht. Search(k) 1 v ← Wurzel 2 while (v ist kein Blatt) { 3 i ← min{1 ≤ j ≤ #Kinder(v) | k ≤ kj } DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 115 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 4 v ← i-tes Kind von v 5 } 6 if (k = key(v)) return True 7 else return False Die Laufzeit ist O(Höhe(T )). Insert (k). Beim Insert wird zuerst einmal wie beim Search ein Blatt gesucht, da an dessen Vater der neue Schlüssel angehängt werden muss. Jetzt muss überprüft werden, ob der Knoten dadurch nicht zu viele Kinder erhält. Wenn ja, muss er und eventuell seine Vorfahren aufgetrennt werden. Im folgenden Algorithmus steht p(v) für den Vater von v. Insert(k) 01 bestimme Blatt v wie in Search 02 w ← p(v) 03 füge k als zusätzliches Blatt von w ein 04 while (#Kinder(w) > b) { 05 if (w 6= Wurzel) y ← p(w) 06 else y ← neue Wurzel mit einzigem Kind w 07 zerteile w in zwei Knoten w 1 und w2 , 09 wobei w1 die b+1 kleinsten und 2 grössten Kinder von w erhält. 10 w2 die b+1 2 11 füge den b+1 -ten Schlüssel von w zu y 2 12 und zwar zwischen die Zeiger auf w1 und w2 13 w ← y //rekursiv weiter machen mit dem Vater 14 } Die Abbildung zeigt einen 2, 3-Baum in der Schlüssel 22 eingefügt werden soll. Der Baum 0 ist der Ausgangsbaum. Wenn nun 22 eingefügt wird, ergibt sich Baum 1 in dem das Blatt, an das 22 angehängt wurde, die Bedingung a ≤ #Kinder ≤ b verletzt. Deshalb wird dieser Knoten aufgespalten (2). Dadurch hat die Wurzel ein Kind zu viel und es wird eine neue Wurzel eingefügt (3). Der Knoten unter der Wurzel wird nun aufgespalten und alles ist in Ordnung. 0 1 4,13 1 1 7 4 7 17,21 13 17 21 4,13 1 24 1 2 7 4 7 1 7 4 7 13 21 17 1 22 21 22 24 22 24 1 7 4 7 17 13 17 22 21 22 24 13 4 21 1 116 17 4,13,21 17 4 1 13 3 4,13,21 1 17,21,22 7 4 7 17 13 17 22 21 22 24 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen Die Laufzeit ist O(Höhe(T )). Delete (k). Zuerst wird der Schlüssel gesucht, wenn er nicht gefunden wird, wird abgebrochen. Wenn er gefunden wurde, wird das Blatt entfernt, das kann dazu führen, dass dem Vater zu wenige Kinder übrig bleiben. 01 02 03 04 05 06 07 08 09 10 11 bestimme Blatt v wie in Search if (key(v) 6= k) return //Schlüssel nicht gefunden w ← p(v) entferne v von w while ((#Kinder(w) < a) ∧ (w 6= Wurzel)) { y ← linker oder rechter Nachbar von w if (#Kinder(y) = a) verschmelze w und y else adoptiere rechtestes bzw. linkestes Kind von y w ← p(w) //rekursiv weiter machen mit dem Vater } if ((w =Wurzel) ∧ (Wurzel hat nur ein Kind)) lösche Wurzel Die Abbildung zeigt einen 2, 3-Baum aus dem das Blatt mit dem Schlüssel 7 gelöscht wird. Der Baum 0 ist der Ausgangsbaum. In Baum 1 wurde das Blatt bereits entfernt, dadurch hat der Knoten 7 ein Kind zu wenig. Jetzt gibt es zwei Möglichkeiten den Knoten y zu wählen. Wird der linke Nachbar gewählt, kommt man zu dem Fall, dass dieser genau a Kinder hat, dann wird verschmolzen (2a). Wird der rechte Nachbarn gewählt, gilt dies nicht, deshalb adoptiert man das linkeste Kind (2b). 0 1 4,13 1 1 7 4 7 2a 17,21 13 17 21 4 1 24 1 4 17 21 24 17,21 13 17 21 24 4,17 1 17,21 13 13 2b 4,13 1,4 1 4,13 1 13 4 13 21 17 21 24 Die Laufzeit ist O(Höhe(T )). Die Laufzeiten der Wörterbuchfunktionen sind von der Höhe des Baumes abhängig. Um die Höhe eines (a − b)-Baumes in Abhängigkeit von n zu bestimmen, muss man sich erstmal überlegen wie viele Schlüssel der Baum der Höhe h speichert. Dieser Wert wird maximal, wenn jeder Knoten genau b Kinder hat. Dann speichert der Baum bh Schlüssel. Minimal wird er, wenn die Wurzel zwei Kinder und alle anderen Knoten a Kinder haben, dann speichert er 2ah−1 Schlüssel. Also gilt: 2ah−1 ≤ n ≤ bh Das nun nach h aufgelöst ergibt n 2 Da die Wörterbuchoperationen alle von der Höhe des Baumes abhängen gilt also für alle die Laufzeit O(log n). Die Wahl der Parameter hängt von dem Speicherort der Daten ab. Wenn die Daten im Hauptspeicher liegen, sollte man a klein, also z. B. 2 oder 3 wählen. Liegen die Daten auf der Festplatte, wählt man a gross, z. B. a = 100. logb n ≤ h ≤ 1 + loga 9.3.25 Was sind AVL-Bäume? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 117 Kapitel 9. Effiziente Algorithmen und Datenstrukturen AVL-Bäume sind balancierte Binäre Suchbäume bei denen für jeden Knoten v gelten muss, dass die Höhe seines linken Unterbaums von der des rechten höchstens um 1 unterscheidet. Für die Laufzeiten der einzelnen Operation gilt: Search Insert Delete O(log n) O(log n) O(log n) Die Komplexität für Search ergibt sich auf folgenden Lemma: AVL-Bäume mit der Höhe h haben mindestens Fib(h + 2) Blätter, also Fib(h + 2) − 1 interne Knoten. Es gilt √ !n 1± 5 1 Fib(n) ≈ √ 2 5 Daher gilt, dass AVL-Bäume mit n Knoten die Höhe O(log n) haben, womit dies auch für Search gilt. Bei Insert und Delete müssen u. U. bestimmte Rebalancierungen durchgeführt werden, deren Worst-Case-Laufzeit aber immer O(log n) ist. 9.3.26 Wovon hängt die Zahl der speicherbaren Element im AVL-Baum ab? Von seiner Höhe. 9.3.27 Vergleichen Sie die verschiedenen Heap-Implementierungen hinsichtlich der Laufzeiten. Make-Heap Insert Minimum Extract-Min Merge Decrease-Key Delete 9.3.28 Binary Heap O(1) O(log n) O(1) O(log n) O(n) O(log n) O(log n) Binomial Heap O(1) O(log n) O(log n) O(log n) O(log n) O(log n) O(log n) Fibonacci Heap O(1) O(1) O(1) O(log n) O(1) O(1) O(log n) Was zeichnet eine sich selbst organisierende Liste aus? Selbstorganisierende Listen verändern die Position der Element entsprechend ihrer Zugriffshäufigkeit um die Zugriffszeiten für häufig benutzte Elemente zu verbessern. Es kommen zwei verschiedene Heuristiken zum Einsatz: 1. Move-To-Front Rule (MFR): Bei jedem Zugriff auf ein Element wird dieses an den Anfang der Liste verschoben. Für die MFR gilt, dass die amortisierten Kosten höchstens um den Faktor 2 schlechter sind als ein optimaler Listenalgorithmus. 2. Transposition Rule (TR): Bei jedem Zugriff auf ein Element wird dieses um eine Position nach vorne verschoben. Die TR kann verglichen mit der MFR oder einem optimalen Algorithmus beliebig schlecht werden. 9.3.29 Was ist ein Leftist-Baum? Ein Leftist-Baum ist ein binärer Suchbaum, so dass für jeden Knoten gilt, dass ein kürzester Weg zu einem Blatt über ein rechtes Kind führt. 9.3.30 Nennen Sie ein Beispiel für sich selbst organisierende Datenstrukturen? Selbst-Organisierende-Listen und Splay-Trees. 9.3.31 118 Ist ein Splay-Tree für eine Englischlexikon geeignet? http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.3. Datenstrukturen Nur wenn man aus irgendwelchen Gründen andauernd die gleichen Wörter nach schaut. 9.3.32 Gibt es Datenstrukturen die Mengenoperationen effizient unterstützen? Ja, Union-Find-Strukturen. 9.3.33 Wie funktionieren Radix-Heaps? Radix-Heaps greifen die Idee von 1-Level Buckets auf, wieder muss die maximale Differenz der Schlüssel C bekannt sein. Im Gegensatz zu den 1-Level Buckets werden bei Radix-Heaps keine festen Bucketgrössen sondern Bucket mit exponentiell zunehmender Grösse verwendet. Dadurch werden nur noch O(log c) Buckets benötigt. Da auch bei den Radix-Heaps die Laufzeit für die teure Extract-Min Operation √ von der Anzahl der Buckets abhängt, verbessern sich die amortisierten Kosten von O(C) bzw. O( C) auf O(log C). 9.3.34 Was sind balancierte Bäume? Suchbäume ohne Balancierung können aus der Form geraten bzw. entarten wobei sich die Laufzeiten für die Zugriffe drastisch verschlechtern können. Balancierte Bäume haben Mechanismen, die dafür sorgen, dass die Bäume nicht oder nur wenig entarten. 9.3.35 Was sind 1-Level Buckets? Buckets bieten eine Möglichkeit Priority Queues zu implementieren. Die Voraussetzung für die Bucket basierten Implementierungen sind: Die Schlüssel sind ganzzahlig. Zu jedem Zeitpunkt gilt, dass die Differenz des grössten und kleinsten Schlüssels ≤ c ist. Dies ist z. B. beim Algorithmus von Dijkstra erfüllt. c ist hier die maximale Kantenlänge. 1-Level Buckets bestehen aus einem Array b[0 . . . C + 1] zur Aufnahme der Buckets. Jedes bi enthält einen Pointer auf die Liste der Elemente im Bucket, einer Zahl minvalue, die gleich dem kleinsten gespeicherten Schlüssel ist, eine Zahl 0 ≤ minpos ≤ c, die den Index des Buckets mit dem kleinsten Schlüssel enthält und einer Zahl n, die die Anzahl der gespeicherten Elemente hält. Die Priority Queue Operationen sind dann wie folgt implementiert. Insert (x). Das Element x wird in b[key(x) mod (C + 1)] eingefügt. Falls key(x) kleiner als der bisher kleinste Wert ist, müssen minvalue und minpos angepasst werden. Ausserdem wird n inkrementiert. Die Laufzeit für diese Operation ist O(1). Extract-Min. n wird decrementiert. Falls noch Elemente übrig bleiben, muss das neue kleinste Element gesucht werden und alle Variablen angepasst werden. Dazu müssen alle Buckets durchlaufen werden. Die Laufzeit ist O(C) Decrease-Key. Zuerst wird der Schlüssel verkleinert. Ergibt sich dadurch ein neuer minvalue, müssen die Variablen angepasst werden. Laufzeit ist O(1). 9.3.36 Was sind 2-Level Buckets? Wenn der Wertebereich sehr gross im Verhältnis zu der Anzahl der abgelegten Werte ist, sind 1-Level Bucket aus zwei Gründen ungünstig. Das Array b belegt statischen Speicherplatz der Grösse O(C) obwohl nur ein kleiner Teil davon gebraucht wird. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 119 Kapitel 9. Effiziente Algorithmen und Datenstrukturen Der Zeitbedarf für Extract-Min nähert sich der Worst-Case-Komplexität von O(C), da der nächste nicht-leere Bucket ziemlich weit entfernt sein kann. 2-Level Buckets versuchen dieses Problem zu beheben, in dem sie eine Bucket-Hierarchie einführen. √ Es gibt ein Top-Bucket (btop) bestehend aus | C + 1|√Buckets und zu jedem Bucket in btop gibt es ein weiteres Bottom-Bucket (bbot), das auch aus B = | C + 1| Buckets besteht. Das i-te bbot nimmt Elemente auf deren Schlüssel im Intervall [iB, (i+1)B] liegen. Um ein Element in ein Bucket einzufügen wird zuerst das passende Bucket in btop gesucht. Dann wird in dem dazugehörigen bbot das Element eingefügt. Um sowohl Zeit als auch Platz zu sparen, kann man durch leichte Modifikation √ nur ein einziges BottomBucket einsetzen. Dann benötigt man nur zwei Arrays der Länge B = | C + 1| + 1. Dabei enthält das Top-Bucket die meisten Elemente in grob vorsortierter Form, nur die Elemente mit den kleinsten Schlüsseln werden im Bottom-Bucket gehalten. Mit√dieser Implementierung erreicht man Laufzeiten von O(1) für Insert sowie Decrease-Key und O( C) für Extract-Min. 9.4 Selektieren und Sortieren 9.4.1 Stellen Sie sich vor, Sie haben ein Feld und wollen das 15. grösste finden. Wie können Sie effektiver als mit Sortieren vorgehen. Mit einem Selektionsalgorithmus. 9.4.2 Wie viele Vergleiche braucht man um von n-Element das Grösste zu finden? Man braucht auf jeden Fall n − 1 Vergleiche. Beweis: Man Betrachte den Algorithmus als Folge von Spielen, die immer das grössere Element gewinnt. Jeder Spieler ausser dem Gewinner muss mindestens einmal verlieren, daher n − 1 Vergleiche. 9.4.3 Welche Komplexität hatte eine naive Bestimmung des Medians? Man verwendet eine normalen Sortieralgorithmus und wählt das mittlere Element. Laufzeit: O(n log n) 9.4.4 i Nennen Sie einen Algorithmus zur effizienten Bestimmung des Median. BFPRT. 9.4.5 Wie ist die untere Schranke für die Median-Bestimmung (mit Beweis)? − 2 Vergleiche. Der Beweis läuft über das Gegenspielerargument. Die untere Schranke is 3n 2 9.4.6 Erklären Sie Radix-Sort. Die Idee bei Radix-Sort ist die Eingabewerte einzeln nach ihren Dezimalstellen zu sortieren und zwar zuerst nach der niedrigsten. Das müssen natürlich nicht Dezimalstellen sein, es können auch Buchstaben oder Binärzahlen verwendet werden. Das Beispiel zeigt wie 3-stellige Dezimalzahlen zuerst nach den Einern , dann nach Zehnern und zuletzt nach den Hundertern sortiert werden. 329 720 720 329 457 355 329 355 657 436 436 436 839 457 839 457 ⇒ ⇒ ⇒ 436 657 355 657 720 329 457 720 355 839 657 839 ↑ ↑ ↑ 120 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.4. Selektieren und Sortieren Die Laufzeit von Radix-Sort hängt natürlich von dem Algorithmus ab, der zum Sortieren nach den einzelnen Stellen verwendet wird. Um n Zahlen nach einer Stelle zu sortieren benötigt man z. B. mit Mergesort O(n log n) Zeit. Falls die Zahlen d Stellen haben, benötigt man insgesamt O(d · n log n), also keine Vorteile zu normalen Sortieralgorithmen. Wenn allerdings ein nicht-vergleichsbasierter Algorithmus wie Counting Sort mit linearer Laufzeit verwendet wird, ergibt sich auch eine lineare Gesamtlaufzeit von Radix-Sort. 9.4.7 Erklären Sie Bucket-Sort. Wenn für alle zu sortierenden Zahlen x, gilt, dass 0 ≤ x < 1 und die Zahlen zufällig verteilt sind, kann Bucket-Sort ein Array mit n Zahlen in Zeit O(n) sortieren. Benötigt werden n Buckets, die jeweils eine Liste von Werte aufnehmen können. Der Algorithmus läuft dann wie folgt ab. 1. Initialisiere Buckets b0 , b1 , . . . , bn−1 . 2. Lege ai in Bucket dn · ai e. 3. Sortiere die Elemente innerhalb der Buckets. 4. Konkateniere die Buckets. Bis auf den Schritt 3 ist alles in O(n) zu erledigen. In Schritt 3 kommt es darauf an, wie wie viele Elemente durchschnittlich in einem Bucket sind. Falls in jedem Bucket nur 1 Elemente ist, ist es einfach. Sei ni die Zufallsvariable die die Anzahl der Element in Bucket i beschreibt. Wenn man in Schritt 3 einen Sortieralgorithmus mit quadratischer Laufzeit verwendet, ist die erwartete Zeit, die Element im Bucket i zu sortieren E O(n2i ) = O(E n2i ). Für alle Bucket ergibt sich daher die Summe: ! n−1 n−1 X X 2 (9.1) E n2i O E ni = O i=0 i=0 Die Wahrscheinlichkeit, dass ein Element x in Bucket i landet ist 1/n. Das Verteilen von n Elementen auf n Buckets kann mit dem Bällen-Urnen-Modell erfasst werden und ist binomialverteilt. Das heisst die Wahrscheinlichkeit, dass ni = k ist, folgt der Binomialverteilung b(k; n, p). Somit gilt für den Erwartungswert und die Varianz 1 =1 n E [ni ] = np = n · Var [ni ] = np(1 − p) = n · 1 1 1 =1− · 1− n n n Für jede Zufallsvariable gilt folgende Gleichung 2 E n2i = Var [ni ] + E [ni ] Daraus ergibt sich E n2i 1 + 12 n 1 = 2− n = O(1) = 1− Wenn man dieses Ergebnis nun in (9.1) einsetzt ergibt sich n−1 X O E i=0 n2i =O n−1 X i=0 O(1) ! und damit eine Gesamtlaufzeit von O(n). 9.4.8 Was versteht man unter Selektion? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 121 Kapitel 9. Effiziente Algorithmen und Datenstrukturen Das Suchen eines n kleinsten Elements in einer Menge. 9.4.9 Warum sortiert man nicht einfach? Sortieren hat grösseren Aufwand als Selektieren. 9.4.10 Wie funktioniert Quicksort? Quicksort ist ein Divide&Conquer Algorithmus mit einer durchschnittliche Laufzeit von O(n log n). Die Worst-Case-Laufzeit ist allerdings O(n2 ). Der grundlegende Algorithmus ist sehr einfach und funktioniert wie folgt. Dieser Algorithmus sortiert das Array A[p . . . r]. Quicksort(A, p, r) 1 if p < r 2 then q ← Partition(A, p, r) 3 Quicksort(A, p, q) 4 Quicksort(A, q + 1, r) Das Eingabearray wird in zwei Teile zerlegt und diese rekursiv mit Quicksort sortiert. Entscheidend ist die Partitionierung des Arrays. Eine einfache Implementierung von Partition funktioniert so. Partition(A, p, r) 1 x ← A[p] 2 i←p−1 3 j ←r+1 4 while (True) { 5 do j ← j − 1 until (A[j] ≤ x) 6 do i ← i + 1 until (A[i] ≥ x) 7 if (i < j) swap A[i] ↔ A[j] 8 else return j 9 } Die Funktion Partition wählt eine Pivot-Element x und vertausche die Array-Elemente so, dass alle Element links von x kleiner und alle rechts davon grösser sind als x. Dafür werden zwei Zeiger i und j verwendet, die gegeneinander laufen. Der entscheidende Punkt ist die Wahl des Pivot-Elements. In dieser Implementierung wird einfach das erste Element des Arrays gewählt. Diese Methode ist nicht optimal. Wenn das Array z. B. bereits sortiert ist ergeben sich bei dieser Wahl immer 1:(n − 1)– Partitionen. Deshalb konzentrieren sich die Verbesserungen von Quicksort darauf das Pivot-Element möglichst geschickt zu wählen. Mann kann es zufällig wählen oder noch besser den Median von drei Zufallswerten wählen. Zur Analyse von Quicksort, siehe 9.4.16. 9.4.11 i Wie funktioniert Mergesort? Mergesort ist ein Divide&Conquer Algorithmus. Eine Feld wird sortiert in dem es in zwei (fast) gleich grosse Teilfelder zerlegt wird, die dann wieder rekursiv mit Mergesort sortiert werden. Die Teilfelder werden dann geMerget um die Gesamtlösung zusammen zu setzen. Divide: Teile das n-elementige, zu sortierende Feld in zwei Teilfelder der Länge n/2. Conquer: Sortiere die zwei Teilfelder rekursiv mit Mergesort. Combine: Verschmelze die zwei sortierten Teilfelder zu einem. Das Verschmelzen der Teilfelder erledigt die Funktion Merge. Anschaulich funktioniert sie wie ein der Algorithmus, den ein Mensch benutzen würde um zwei sortierte Kartenstapel zu verschmelzen. Beide Stapel liegen mit der kleinsten Karte oben auf dem Tisch. Man wählt nur immer die kleinste Karte die oben aufliegt und legt sie nacheinander auf einen dritten Stapel. Der dritte Stapel ist dann eine korrekte Verschmelzung der beiden Stapel. Dieser Algorithmus lässt sich in O(n) implementieren, wobei n die Summe der Längen der beiden Teilfelder ist. Mit der Merge-Funktion sieht der Mergesort-Algorithmus so aus: 122 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.4. Selektieren und Sortieren Merge-Sort(A, p, r) 1 if p < r 2 then q ← b(p + r)/2c 3 Merge-Sort(A, p, q) 4 Merge-Sort(A, q + 1, r) 5 Merge(A, p, q, r) Diese Funktion sortiert das Teilfeld A[p . . . r]. Falls p > r gibt es nicht mehr zu sortieren und die Funktion bricht ab. Merge (A, p, q, r) verschmelzt die Felder A[p . . . q] und A[q + 1 . . . r]. 9.4.12 i Komplexität von Mergesort herleiten. Mergesort ist ein typischer Divide&Conquer-Algorithmus. Die Laufzeit T (n) von Divide&ConquerAlgorithmen kann rekursiv ausgedrückt werden: O(1) wenn n ≤ c , T (n) = αT (n/β) + D(n) + C(n) sonst. Wobei c eine konstante ist, für die die Berechnung trivial wird. Das Problem wird in α Teilproblem geteilt und des Teilproblem hat die Grösse n/β, d. h. das die Teilprobleme sich auch überlappen können. D(n) ist die Zeit, die man braucht um das Problem in Teilprobleme zu zerlegen, C(n) die Zeit um die Lösung aus den Teillösungen zusammenzusetzen. Die Rekursion für Mergesort ergibt sich wie folgt: Divide: Dieser Schritt muss die Mitte des Subarrays finden, daher D(n) = O(1). Conquer: Das Problem wird rekursiv in 2 Teilprobleme zerlegt, die beide die Grösse n/2 haben, also 2T (n/2). Combine: Merge benötigt für n Elemente O(n), also C(n) = O(n). Daraus ergibt sich: T (n) = O(1) 2T (n/2) + O(n) wenn n = 1 , sonst. Der erste Fall ergibt sich, weil ein Feld der Länge 1 bereits sortiert ist. Das O(1) für Divide trägt zur Summe nichts bei, also fällt es unter den Tisch. Für diese Rekursionsgleichung kann nun mit dem Mastertheorem (9.1.6) die Laufzeit bestimmt werden. α = 2, β = 2 und f (n) = O(n), daher trifft der zweite Fall des Mastertheorems zu und die Laufzeit von Mergesort ist Θ nlogβ α log n = Θ(n log n) . 9.4.13 Wie funktioniert Heapsort? Die n zu sortierenden Element werden nacheinander auf den Heap gelegt. Danach werden alle n Element wieder mit Extract-Min herunter geholt. Dies kann auch in-situ geschehen. Laufzeit ergibt sich aus den Laufzeiten der Heapoperationen und ist O(n log n). 9.4.14 Wie funktioniert BFPRT? Der Algorithmus Select zur Bestimmung des i-kleinsten Elements einer Menge S, läuft in folgenden Schritten ab. 1. Teile die n Elemente in bn/5c Gruppen zu je 5 Elementen plus eine Gruppe mit n mod 5 Elementen. 2. Jetzt wird der Median jeder der dn/5e Gruppen bestimmt in dem die Elemente der Gruppen per Insertion Sort sortiert werde und der mittlere Wert gewählt wird. Falls die Gruppe ein ungerade Anzahl an Elementen hat, wird der grössere der beiden Medianwerten gewählt. 3. Jetzt wird Select rekursiv aufgerufen um den Median der in Schritt 2 berechneten Median zu bestimmen. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 123 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 4. Jetzt werden die Element des Eingabearrays bzgl. des Medians der Mediane s partitioniert. Dadurch ergeben sich zwei Mengen S1 = {x ∈ S | x < s} und S2 = {x ∈ S | x > s} 5. Jetzt gibt es drei Fälle: Falls i ≤ |S1 |, bestimme rekursiv das i-kleinste Element in S1 Falls i = |S1 | + 1, gib s als Lösung zurück. Sonst, bestimme rekursiv das (i − |S1 | − 1)-kleinste Elemente in S2 . 9.4.15 Wie ist die Laufzeit von BFPRT? Um die Laufzeit zu bestimmen, muss man zuerst eine untere Grenze für die Elemente, die grösser als das Partitionierungselement s sind, bestimmen. Mindestens die Hälfte der Mediane, die in Schritt 2 gefunden werden, ist grösser oder gleich dem Median der Mediane s. Also sind aus der Hälfte der dn/5e Gruppen jeweils drei Element grösser als s. Ausnahmen sind die Gruppe, die s enthält und die letzte Gruppe, falls n nicht durch 5 teilbar ist. Wenn man diese beiden Gruppen weg lässt, ergibt sich für die Anzahl der Elemente, die grösser als s ist l m 3n 1 n − 6. −2 ≥ 3 2 5 10 Ebenso gilt, dass die Anzahl der Elemente, die kleiner als s sind, mindestens 3n/10 − 6 ist. Daher wird im Worst-Case Select 7n/10 + 6 mal rekursiv aufgerufen (Schritt 5). Die in der Abbildung grau unterlegten Elemente sind die, die grösser als s sind. s Damit lässt sich nun eine Rekursionsgleichung erstellen. Schritt 1, 2 und 4 benötigen O(n). Schritt 3 benötigt T (dn/5e) und Schritt 5 benötigt höchstens T (7n/10 + 6). Für n > 20 gilt 7n/10 + 6 < n und für n ≤ 80 ist die Laufzeit O(1). Daher ergibt sich die Rekursionsgleichung: O(1), wenn n ≤ 80, T (n) ≤ T (dn/5e) + T (7n/10 + 6) + O(n) wenn n > 80. Die lineare Laufzeit wird nun durch die Substitutionsmethode gezeigt. Man nimmt an, dass T (n) ≤ cn ist und Substitute entsprechend. T (n) ≤ cdn/5e + c(7n/10 + 6) + O(n) ≤ cn/5 + c + 7cn/10 + 6c + O(n) ≤ 9cn/10 + 7c + O(n) ≤ cn Der letzte Schritt gilt, da man c so gross wählen kann, dass es grösser als O(n) für alle n > 80 ist. 9.4.16 Rekursionsformel für Quicksort? Die Rekursionsformel hängt von der Güte der Partitionierung ab. Im schlechtesten Fall teil Quicksort immer in eine Partition der Grösse 1 und eine Partition der Grösse n − 1. Dann ergibt sich T (n) = T (n − 1) + O(n) und dafür gilt T (n) = O(n2 ). 124 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.4. Selektieren und Sortieren Im besten Fall teil Quicksort immer in zwei Partitionen der Grösse n/2 auf, dann gilt T (n) = 2T (n/2) + O(n) was laut Master Theorem O(n log n) ist. Falls Quicksort in jedem Schritt zwei Partitionen, deren Grössen konstant proportional zueinander sind (z. B. immer 9:1), erzeugt, so ergibt sich die Rekursionsgleichung T (n) = T (9n/10) + T (n/10) + O(n) (für das Beispiel) Egal wie diese Verhältnis aussieht, also z. B. auch 99:1, ergibt sich die Laufzeit O(log n), weil der Rekursionsbaum immer Tiefe O(log n) hat und jede Ebene O(n) kostet. Daher ergibt sich auch für die durchschnittliche Laufzeit von Quicksort O(n log n). Mann muss nur dafür sorgen, dass die Partitionierung möglichst zufällig abläuft. 9.4.17 Gibt es Sortierverfahren mit einer besseren Laufzeit als O(n log n). O(n log n) ist die untere Schranke für vergleichsbasiertes Sortieren. Wenn ohne Vergleiche sortiert wird, können bessere Laufzeiten erreicht werden. Beispiele sind Radix- und Bucket-Sort die aber bestimmte Annahmen über die Daten machen. Die untere Schranke für vergleichsbasiertes Sortieren kann über den Entscheidungsbaum hergeleitet werden. Wenn n Element sortiert werden müssen, gibt es n! verschieden Permutationen, also hat der Entscheidungsbaum n! Blätter. Die Länge des längsten Pfades von der Wurzel zu einem Blatt entspricht der Worst-Case-Laufzeit beim Sortieren. Deshalb muss man nur die Höhe h des Baumes bestimmen um die untere Schranke für Sortieren zu finden. Da ein Binär (Entscheidungsbaum ist binär) nicht mehr als 2h Blätter hat gilt n! ≤ 2h . Daraus ergibt sich durch Logarithmieren h ≥ log n! . Mit der Stirling-Approximation für die Fakultät kommt man zu h ≥ = = = 9.4.18 log n n e n log n − e log e n log n − O(1) O(n log n) Braucht Quicksort zusätzlichen Speicher? Ausser ein paar wenigen Zeigern, nein. 9.4.19 Wie kann man bei Quicksort die Worst-Case-Laufzeit vermeiden? Mann muss dafür sorgen, dass die Partitionierung möglichst gut ist und nicht in zwei Partitionen teilt, bei der eine Partition nur die Grösse 1 hat. Dazu kann man eine randomisierte Version benutzten, die das Pivot-Element immer zufällig auswählt. Das kann noch weiterhin verbessert werden, wenn man 3 Element zufällig wählt und den Median dieser als Pivot-Element verwendet (Median-of-Three). 9.4.20 Warum verwendet man in der Praxis Mergesort häufiger? Mergesort sortiert Elemente, die bereits richtig sind, nicht. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 125 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 9.5 Minimale Spannbäume 9.5.1 Wie funktioniert der Algorithmus von Kruskal? Der Algorithmus von Kruskal ist ein Greedy-Algorithmus zur Bestimmung eines minimalen Spannbaumes. Der Algorithmus sortiert die Kanten nach ihrem Gewicht und fängt dann mit der kleinsten Kante an diese in den Spannbaum einzufügen. Bei jedem Einfügen überprüft er, ob durch das Einfügen ein Kreis entsteht, falls ja, wird die Kante ignoriert, falls nein, wird die Kante eingefügt. Meist wird der Algorithmus nicht mit einer Kantenvorsortierung sondern einer Priority-Queue implementiert. Dies kann Vorteile haben, wenn die grössten Kanten des Graphen nicht Teil des minimalen Spannbaums sind. Bei der Implementierung mit Priority-Queue kann der Algorithmus in diesem Fall frühzeitig abbrechen. Zur Kreisüberprüfung wird eine Union-Find-Struktur verwendet. Zu Beginn ist jeder Knoten in einer eigenen Menge. Wenn eine Kante eingefügt werden soll, wird überprüft, ob die beiden Endknoten in unterschiedlichen Menge liegen, wenn nicht, wird die Kante ignoriert. Falls dem so ist, wird die Kante eingefügt und die beiden Mengen verschmolzen. Implementierung mit PQ: Kruskal 01 A ← ∅ 02 Q ← E[G] 03 for each (vertex v ∈ V [G]) { 04 Make-Set(v) 05 } 06 while (Q 6= ∅) { 07 (u, v) ← Extract-Min(Q) 08 if (Find-Set(u) 6= Find-Set(v)) { 09 A ← A ∪ {(u, v)} 10 Union(u, v) 11 } 12 } 13 return A Dieser Algorithmus hat gegenüber dem mit Kantenvorsortierung keinen Vorteil, da er nicht frühzeitig abbricht. Theoretisch kann man abbrechen, wenn nur noch eine einzige Menge übrig ist, dann kann sowieso keine weitere Kante mehr hinzugefügt werden. Für die Priority-Queue Operationen ergibt sich die Laufzeit O(m log m), die Union-Find-Operationen haben eine sehr kleine (amortisierte) Komplexität und tragen damit unerheblich zu der Gesamtlaufzeit bei, also hat der Algorithmus von Kruskal die Laufzeit O(m log m). 9.5.2 Wie ist der Dijkstra-Algorithmus mit dem von Prim vergleichbar? Es handelt sich praktisch um den gleichen Algorithmus. Bei Dijkstra müssen zusätzlich die Pfadlängen von Startpunkt addiert werden. 9.5.3 Welche Algorithmen gibt es zur Berechnung minimaler Spannbäume? Es gibt die Algorithmen von Kruskal und Prim. 9.5.4 Erklären Sie den Algorithmus von Prim. Der Algorithmus von Prim zur Bestimmung es minimalen Spannbaums geht wie folgt vor. Alle Knoten des Baumes werden in einer Priority Queue verwaltet. Zu Beginn erhalten alle Knoten den Wert ∞, nur der Startknoten erhält 0. Nun nimmt man sich den Knoten u mit dem kleinsten Wert aus der Queue (beim ersten mal der Startknoten) und durchläuft man alle Nachbarknoten v des Knotens u, die noch in der Queue sind. Ist das Kantengewicht von (u, v) kleiner als der beim Knoten gespeicherte Wert key[v], wird der Knotenwert aktualisiert. 126 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.6. Kürzeste Pfade Prim (G, w, r) 01 Q ← V [G] 02 for each (u ∈ Q) { 03 key[u] ← ∞ 04 } 05 key[r] ← 0 06 π[r] ←NIL 07 while (Q 6= ∅) { 08 u ← Extract-Min(Q) 09 for each (v ∈ Adj[u]) { 10 if (v ∈ Q ∧ w(u, v) < key[v]) { 11 π[v] ← u 12 key[v] ← w(u, v) 13 } 14 } 15 } Der Algorithmus speichert zusätzlich für jeden Knoten seinen Vorgänger im Feld π[]. Laufzeit: Die Schleife in den Zeilen 7–15 wird n mal durchlaufen, darin wird jedes mal ein ExtractMin ausgeführt, also n log n. Die Schleife in den Zeilen 9–13 wird O(m) mal durchlaufen. Also ergibt sich insgesamt O(m+n log n). Dies gilt nur, wenn die Priority Queue mit Fibonacci Heaps implementiert wird, da bei diesen die Zuweisung in Zeile 12 in O(1) durchgeführt werden kann. 9.5.5 Was ist ein Spannbaum? Für einen Graphen G(V, E) ist G0 (V, E 0 ) mit E 0 ⊆ E ein Spannbaum, wenn G0 ein Baum ist. 9.5.6 Was ist eine minimaler Spannbaum, was hat er für einen praktischen Nutzen? Ein minimaler Spannbaum ist ein Spannbaum des Kantengewicht (Summe der Gewichte aller Baumkanten) minimal ist. In der Praxis kann man so z. B. die billigste Leitungskonfiguration zwischen Rechnern finden, so dass diese gerade noch verbunden sind. 9.5.7 Was sind die blaue und die rote Regel? Diese Regeln werden bei den Korrektheitsbeweisen für minimale Spannbaum Algorithmen benutzt. 9.6 Kürzeste Pfade 9.6.1 i Wie funktioniert Algorithmus von Dijkstra? Dijkstras Algorithmus zur Bestimmung der kürzesten Pfade ähnelt dem von Prim zur Bestimmung des minimalen Spannbaumes sehr stark. Wieder werden alle Knoten in einer Priority Queue verwaltet und ihr Entfernungswert zu Beginn auf ∞ gesetzt. Der Startknoten erhält den Wert 0. Nun wird der kleinsten Knoten u aus der Queue entnommen und alle seine Nachbarn v durchlaufen. Gibt es von u nach v einen kürzeren Weg als in v gespeichert, wird der Wert bei v aktualisiert. Dijkstra (G, w, s) 01 for each (v ∈ V [G]) { 02 d[v] ← ∞ 03 π[v] ←NIL 04 } 05 d[s] ← 0 06 Q ← V [G] 07 while (Q 6= ∅) { DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 127 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 08 09 10 11 12 13 14 15 } u ← Extract-Min(Q) for each (v ∈ Adj[u]) { if (d[v] > d[u] + w(u, v)) { d[v] ← d[u] + w(u, v) π[v] ← u } } Der Algorithmus speichert zusätzlich für jeden Knoten seinen Vorgänger im Feld π[]. Laufzeit: Auch hier zeigt sich die Verwandtschaft zum Algorithmus von Prim. Die Schleife in den Zeilen 7–15 wird n mal durchlaufen, darin wird jedes mal ein Extract-Min ausgeführt, also n log n. Die Schleife in den Zeilen 9–13 wird O(m) mal durchlaufen. Also ergibt sich insgesamt O(m + n log n). Dies gilt nur, wenn die Priority Queue mit Fibonacci Heaps implementiert wird, da bei diesen die Zuweisung in Zeile 11 in O(1) durchgeführt werden kann. 9.6.2 Was ist das Single-Source-Shortest-Path-Problem? Man möchte die Entfernung von einem Startknoten zu allen anderen Knoten wissen. 9.6.3 Komplexität von Dijkstra? Es werden n−1 Schritte gemacht in denen jeweils eine Extract-Min- und für alle Nachbarn des Knoten evtl. eine Insert- oder Decrease-Key-Operation durchgeführt. Wenn die PQ mit Fibonacci-Heaps implementiert ist, beträgt die Laufzeit O(n log n + m). 9.6.4 Welche Verfahren zur Bestimmung der transitiven Hülle kennen Sie? Man kann alle Kantengewichte auf 1 setzen und den Floyd-Warshall-Algorithmus laufen lassen. Gibt es einen Pfad zwischen zwei Knoten steht in der Matrix eine Zahl n ansonsten ∞. 9.6.5 Nennen Sie mir einen Algorithmus zur effizienten Matrizen-Multiplikation. Der Strassen-Algorithmus. 9.6.6 Welche Algorithmen gibt es ausser Dijkstra für SSSP? Bellman-Ford. 9.6.7 Welche Algorithmen gibt es für das All-Pairs-Shortest-Path-Problem? Floyd-Warshall. 9.6.8 Wie kann man den Floyd-Warshall-Algorithmus so modifizieren, dass man auch die Pfade ablesen kann? Man kann in einer Vorgängermatrix für jeden Knoten den Vorgänger speichern. 9.6.9 Welche Komplexität hat der Floyd-Warshall-Algorithmus? Er Algorithmus besteht aus 3 ineinander geschachtelten For-Schleifen und daher Laufzeit O(n 3 ). Der Algorithmus ist dem CYK-Algorithmus sehr ähnlich, der dieselbe Laufzeit hat. 9.6.10 Wie kann man Zyklen in Graphen bestimmen? Ein Graph enthält genau dann Zyklen, wenn während der Tiefensuche Rüeckwärtskanten gefunden werden. 128 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 9.7. Matchings 9.6.11 i Erklären Sie den Strassen-Algorithmus zur Matrizen-Multiplikation. Normalerweise wird die Multiplikation zweier n × n Matrizen wie folgt berechnet. b11 b12 a11 a12 c11 c12 · = b21 b22 a21 a22 c21 c22 a11 b11 + a12 b21 a11 b12 + a12 b22 = a21 b11 + a22 b21 a21 b12 + a22 b22 Das Kostet 8 Multiplikation (O(n3 )) und 4 (O(n2 )) Additionen. Wenn man einen Divide&ConquerAlgorithmus angibt, der die n × n Matrizen immer wieder in n/2 × n/2 Matrizen zerlegt und dabei die klassische Form der Berechnung anwendet, erhält man die Rekursionsformel T (n) = 8T (n/2) + O(n2 ) Laut Mastertheorem gilt für T (n) = O(n3 ). Also wurde durch Divide&Conquer nichts gewonnen, im Vergleich zur normalen Matrizen-Multiplikation. Bei Strassens Methode wird die Anzahl der Multiplikationen auf 7 gedrückt. Man geht wie folgt vor. Zuerst werden sieben Produkte gebildet. m1 m2 m3 := := := m4 m5 := := m6 m7 := := c1 = c2 c3 c4 = = = (a12 − a22 )(b21 + b22 ) (a11 + a22 )(b11 + b22 ) (a12 − a21 )(b11 + b12 ) (a11 + a12 )b22 a11 (b12 − b22 ) a22 (b21 − b11 ) (a21 + a22 )b11 Mit diesen Produkten ergibt sich: m 1 + m2 − m4 − m6 m 4 + m5 m 6 + m7 m 2 − m3 Wenn der Divide&Conquer-Algorithmus Strassens Methode verwendet, ergibt sich T (n) = 7T (n/2) + O(n2 ) und für das gilt laut Mastertheorem T (n) = O(nlog 7 ) = O(n2,81 ). Der Algorithmus von Strassen ist also tatsächlich schneller. 9.7 Matchings 9.7.1 Was ist ein Matching? Eine Matching ist eine Kantenmenge, wobei kein Knoten Endpunkt von mehr als einer Kante ist. 9.7.2 Welche Matchings sind interessant? Interessant sind Matchings mit maximaler Kardinalität. Gibt es mehrere solche Matchings interessiert man sich für Matchings maximaler Kardinalität mit minimalen bzw. maximalen Gewicht. 9.7.3 Wie findet man Matchings? Man sucht nach augmentierenden Pfaden. Solange es noch einen augmentierenden Pfad gibt, kann man diesen Invertieren und erhöht damit die Kardinalität des Matchings. Wenn es keine augmentierenden Pfade mehr gibt, ist die Kardinalität des Matchings maximal. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 129 Kapitel 9. Effiziente Algorithmen und Datenstrukturen 9.7.4 Was ist ein augmentierender Pfad? Wie findet man ihn? Ein augmentierender Pfad ist ein Pfad dessen Kanten abwechselnd gemacht und ungematcht sind. Die erste und die letzte Kante müssen ungematcht sein. Im bipartiten Graphen können augmentierende Pfade mit einer modifizierten Breitensuche gefunden werden. 130 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 Kapitel 10 Automaten, Formale Sprachen und Berechenbarkeit 10.1 10.1.1 Allgemeines i Was ist die Chomsky-Hierarchie? Eine hierarchische Einteilung formaler Sprachen. 10.1.2 Möglichkeiten Sprachen zu Erzeugen? Grammatiken und Turing-Generator-Maschinen. 10.1.3 Was ist Abzählbarkeit? Eine unendliche Menge M ist abzählbar wenn es eine bijektive Abbildung von N nach M gibt: M = {f (1), f (2), f (3), . . .} Diese Funktion muss nicht zwangsweise berechenbar sein. 10.1.4 Kann man aus einer Grammatik schliessen, was für ein Sprachtyp beschrieben wird? Ja. Die Produktion der verschiedenen Sprachtypen unterliegen bestimmten Regeln. 10.1.5 Was ist der Homomorphismus bei formalen Sprachen? Wie Schnitt, Vereinigung, usw. ist auch ein Homomorphismus eine Operation auf formalen Sprachen. Sei h : Σ → ∆∗ eine Abbildung, also Zeichen → Zeichenkette. Man kann die Abbildung zu einem Homomorphismus h : Σ∗ → ∆∗ erweitern, also Zeichenkette → Zeichenkette. Die folgenden Schritte Zeigen die zeichenweise Anwendung der Abbildung. h() h(a) h(aw) = = h(a) = h(a)h(w) Für eine Sprache L ⊆ Σ∗ heisst h = {h(w) | w ∈ L} ⊆ ∆∗ homomorphes Bild von L. Entsprechend ist (−1) h = {x ∈ Σ∗ | h(x) = w, w ∈ L} das Urbild einer Sprache L ⊆ ∆∗ . Man nennt h Homomorphismus. 10.1.6 (−1) den inversen Was sind Äquivalenzklassen? Was haben Sie mit formalen Sprachen zu tun? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 131 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit Jede Äquivalenzrelation R teilt eine Menge in mehrere disjunkte Mengen. Die Anzahl dieser Mengen heisst der Index von R. Man kann jeder Sprache die Äquivalenz Relation RL auf Σ∗ zuweisen. Es gilt xRL y genau dann, wenn für alle Wörter z ∈ Σ∗ gilt xz ∈ L ⇔ yz ∈ L Der Satz von Myhill, Nerode besagt, dass eine Sprache genau dann regulär ist, wenn der Index der von RL endlich ist. Auf diesem Satz beruht auch die Existenz des Minimalautomaten, der ja auch Äquivalenzklassenautomat heisst. 10.2 10.2.1 Chomsky 3 Beschreiben die regulären Ausdrücke (a∗ a∗ ) und (a∗ |a∗ )∗ die selbe Sprache? Beide Ausrücke beschreiben die Sprache, die durch den Ausdruck a∗ beschrieben wird. 10.2.2 Geben Sie einen endlichen Automaten für (a∗ |a∗ )∗ an. Der Automat, der a∗ akzeptiert, sieht so aus: a z0 Der Automat A = (Q, Σ, δ, I, F ) ist definiert mit Q = {z0 } Σ = {a} I = {z0 } δ F 10.2.3 = {(z0 , a, z0 )} = {z0 } Was sind reguläre Ausdrücke? Reguläre Ausdrücke sind Formeln zur Beschreibung regulärer Sprachen. Sind sind induktiv definiert: ∅ ist ein regulärer Ausdruck ist ein regulärer Ausdruck jedes a ∈ Σ ist ein regulärer Ausdruck sind α und β reguläre Ausdrücke, so ist auch αβ ein regulärer Ausdruck sind α und β reguläre Ausdrücke, so ist auch α|β ein regulärer Ausdruck ist α ein regulärer Ausdruck, so ist auch α∗ ein regulärer Ausdruck 10.2.4 Wie zeigt man, dass der Schnitt zweier regulärer Sprachen wieder eine reguläre Sprache ist? Mit Hilfe der folgenden zwei Aussagen und des Gesetzes von De Morgan lässt sich die Abgeschlossenheit unter Schnitt beweisen. 1. Reguläre Sprachen sind unter der Vereinigung abgeschlossen. Dies gilt, weil die regulären Ausrücke genau die regulären Sprachen beschreiben und die Vereinigung eine der Operation bei regulären Ausdrücken ist. 2. Reguläre Sprachen sind unter Komplementbildung abgeschlossen. Dies kann durch einen komplementären Automaten gezeigt werden. Vertauscht man bei einem Automaten A Endzustände mit Nicht-Endzuständen, so erhält man einen Automaten A0 , der die Komplementärsprache erkennt. 132 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.2. Chomsky 3 Mit dem Gesetz von De Morgan ergibt sich für zwei Sprachen L1 und L2 : L1 ∪ L 2 = L 1 ∩ L 2 10.2.5 Zeigen Sie, dass das Komplement bei regulären Sprachen abgeschlossen ist. siehe 10.2.4 10.2.6 Welchen Typ hat die Sprache a3j+2 bi+1 , j, i ≥ 0? Geben Sie einen Automaten dazu an. Da i und j nicht voneinander abhängig sind, handelt es sich um eine reguläre Sprache, ein entsprechender Automat für i = j = 0 sieht so aus: z0 10.2.7 a z1 a z2 b z3 Was für Varianten von endlichen Automaten gibt es? Ein endlicher Automat A = (Q, Σ, δ, I, F ) heißt: 1. Automat mit einem Startzustand wenn gilt |I| = 1. 2. alphabetisch, wenn gilt δ ⊆ Q × (Σ ∪ {}) × Q, d. h. jeder Übergang ist mein genau einem Zeichen oder markiert 3. buchstabierend, wenn gilt δ ⊆ Q×Σ×Q, d. h. jeder Übergang ist mit genau einem Zeichen markiert, ist nicht erlaubt 4. deterministisch (DEA), wenn er buchstabierend ist, genau einen Startzustand hat und für alle p, q, r ∈ Q und alle a ∈ Σ mit (p, a, q) ∈ δ und (p, a, r) ∈ δ gilt q = r, d. h. von jedem Zustand gibt es jeweils nur einen Übergang mit der Markierung a. 5. vollständig, wenn für jedes p ∈ Q und jedes a ∈ Σ ein q ∈ Q mit (p, a, q) ∈ δ existiert, d. h. von jedem Zustand gibt es für jeden Buchstaben des Alphabets einen Übergang. 10.2.8 Sind reguläre Sprachen unter Homomorphismen abgeschlossen? Ja. Der Beweis ergibt sich aus dem Beweis für die Substitution. Dieser Beweis läuft induktiv über reguläre Ausdrücke und Arbeitet auf der syntaktischen Repräsentation der Sprache. 10.2.9 i Wie wandelt man einen endlichen Automaten in einen regulären Ausdruck um? Man verwandelt den Automaten schrittweise in einen verallgemeinerten Automaten an dessen Kanten reguläre Ausrücke stehen. Das mach man solang bis nur noch zwei Zustände, ein Startzustand und ein Endzustand übrig bleiben. Auf der einzigen verbleibenden Transition steht der gesuchte reguläre Ausdruck. Beispiel, Elimination des Zustandes qi : ac*d a b c qi d bc*d ac*e e f bc*e|f 10.2.10 Pumping-Lemma erklären und beweisen. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 133 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit Sei L eine reguläre Sprache. Dann gibt es eine Zahl n, so dass sich alle Wörter x ∈ L mit |x| ≥ n zerlegen lassen in x = uvw, so dass folgende Eigenschaften erfüllt sind: 1. |v| ≥ 1, 2. |uv| ≤ n, 3. für alle i = 0, 1, 2, . . . gilt: uv i w ∈ L Mit Hilfe des Pumping Lemmas lässt sich zeigen, dass eine Sprache nicht regulär ist. Der Beweis läuft über die Konstruktion eines Automaten: Da L regulär ist, gibt es einen Automaten A, der L erkennt. Für die Zahl der Zustände Q wählt man |Q| = n. Ein beliebiges Wort x hat die Länge ≥ n, daher durchläuft der Automat beim Abarbeiten des Wortes |x| + 1 Zustände (wenn man den Startzustand mit zählt). Mit dem Schubfachprinzip kann man schliessen, dass diese Zustände nicht alle verschieden sein können (|x| ≥ n), d. h. irgendwann muss der Automat eine Schleife durchlaufen. Jetzt wählt man die Zerlegung x = uvw so, dass Zustand nach dem Lesen von u und uv derselbe ist. Diese Zerlegung kann so gewählt werden, dass Bedingung 1 und 2 erfüllt werden. Da die Zustände die selben sind, erreicht der Automat beim Lesen des Wortes uw denselben Endzustand wie beim Lesen von x = uvw. Das heisst uw = uv 0 w ∈ L. Dasselbe gilt für uvvw = uv 2 w, uvvvw = uv 3 w, usw. Damit ist auch Bedingung 3 erfüllt. 10.2.11 Pumping-Lemma auf Sprache der Palindrome anwenden. Man nimmt an, dass die Sprache der Palindrome regulär ist und führt das mit dem Pumping Lemma zum Widerspruch. Man wählt ein Wort x wie folgt: n n z }| { z }| { x = aa . . . aa b aa . . . aa Nun sei x = uvw die den Bedingungen 1 und 2 des Pumping Lemmas entspricht . . aa} abaa . . . aa} x = aa . . aa} |aa .{z | {z | .{z u Wegen |v| ≥ 1 müsste dann auch v w ≤n n z }| { z }| { uw = aa . . . aa b aa . . . aa ein Wort der Sprache sein. Dies ist jedoch kein Palindrom, da das Wort mehr a’s als b’s enthält. Also kann die Sprache der Palindrome nicht regulär sein. 10.2.12 Pumping-Lemma auf ai bi anwenden. Man nimmt an, dass L = {ai bi | i ≥ 0} regulär ist und führt das mit dem Pumping Lemma zum Widerspruch. Nun wählt man das Pumping Lemma n und betrachtet genau das Wort an bn mit der Länge 2n. Für die Zerlegung uvw ergibt sich: 1. v ist nicht leer (Bedingung 1) 2. uv und v können nur aus a’s bestehen (Bedingung 2) 3. wegen Bedingung 3 müsste dann aber auch das Wort uv 0 w = uw = an−|v| bn in der Sprache sein. Diese widerspricht der Definition von L, daher ist L nicht regulär. 10.2.13 Was ist der Unterschied zwischen einem NEA und DEA? siehe 10.2.7 10.2.14 Welche Form haben die Produktionen bei regulären Sprachen? Für jede Produktion l → r muss gelten |l| ≤ |r|, l muss eine einzige Variable sein, r ist ein Terminal oder ein Terminal gefolgt von einer Variablen. 134 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.2. Chomsky 3 10.2.15 Wie funktioniert die Umwandlung von einem nicht-deterministischen Automaten zu einem deterministischen Automaten? Die Umwandlung von einem NFA zu einem DFA läuft in 4 Schritten ab 1. endlicher Automat −→ Automat mit einem Startzustand Man fügt einen neuen Startzustand q0 hinzu. Von diesem Zustand gibt es einen -Übergang zu allen alten Startzuständen. 2. Automat mit einem Startzustand −→ alphabetischer Automat mit einem Startzustand Ein Übergang mit einem Wort w = a1 a2 . . . an wird in mehrere Übergänge, die jeweils nur mit einem Zeichen markiert sind, aufgeteilt. Dazu müssen n − 1 Zwischenzustände eingeführt werden. 3. alphabetischer Automat mit einem Startzustand −→ buchstabierender Automat mit einem Startzustand (-Elimination) Beim Entfernen der -Übergänge muss sicher gestellt werden, dass die akzeptierte Sprache sich nicht ändert. Die -Elimination gliedert sich in drei Teilschritte. a) Für jede Kette von -Übergängen von p nach r gefolgt von einem a-Übergang von r nach q fügt man einen a-Übergang von p nach r hinzu. b) Falls von p eine Kette von -Übergängen zu einem Endzustand führt, wird p auch zum Endzustand. c) Jetzt kann man die -Übergängen löschen. 4. buchstabierender Automat mit einem Startzustand −→ deterministischer endlicher Automat (Potenzautomatenkonstruktion) Die Idee ist, alle Wege im buchstabierenden Automaten gleichzeitig zu Verfolgen und sich die Zustände zu merken, in denen sich der Automat befinden könnte. Dazu verwendet man die Potenzautomatenkonstruktion. Für einen buchstabierenden Automaten A = (Q, Σ, δ, q0 , F ) definiert man einen Automaten A0 = (P(Q), Σ, δ 0 , {q0 }, F 0 ), wobei P(Q) die Potenzmenge von Q ist. Die Übergangsrelation δ 0 definiert man so: (P, a, R) ∈ δ 0 gdw. R = {r ∈ Q | ∃p ∈ P : (p, a, r) ∈ δ} wobei P und R Teilmengen von Q also Element von P(Q) bzw. Zustände des Potenzautomaten sind. Die Menge der Endzustände ist definiert durch F 0 = {P ⊆ Q | P ∩ F 6= ∅}, d. h. diejenigen Teilmengen von Q, die mindestens einen Endzustand enthalten. Der entstandene Automat A0 ist meist nicht minimal, er kann aber mit dem Myhill-Verfahren minimalisiert werden (10.2.18). 10.2.16 Sind reguläre Sprachen gleich den linearen Sprachen? Eine Grammatik heisst rechtslinear wenn alle Produktionen die Form A → wB oder A→w haben. Sie heisst linkslinear wenn alle Produktionen die Form A → Bw oder A→w haben. Eine Sprache L ist genau dann regulär, wenn es eine rechtslineare oder linkslineare Grammatik G mit L = L(G) gibt. Um dies zu Beweisen, kann man zeigen, dass es für jede lineare Grammatik einen endlichen Automaten gibt, der diese Sprache erkennt. In der anderen Richtung muss man zeigen, dass für jede reguläre Sprache einen lineare Grammatik gibt. 10.2.17 Wie zeigen Sie die Äquivalenz zweier regulärer Sprachen? Von beiden Sprachen den Minimalautomaten bauen. Diese beiden Automaten müssen bis auf Isomorphie gleich sein, wenn die Sprachen äquivalent sind. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 135 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit 10.2.18 Algorithmus zur Minimalisierung von DEAs. Zuerst kann man Zustände, die vom Startzustand aus nicht erreichbar sind, streichen. Dann bestimmt man Paare von äquivalenten Zuständen. Dazu bestimmt man iterativ alle nicht-äquivalenten Zustände, in dem man die folgenden Regeln anwendet, bis sich keine Änderungen mehr ergeben. Wenn von zwei Zuständen einer ein Endzustand ist und der andere nicht, so sind die Zustände nicht äquivalent p∈F ∧q ∈ /F ⇒ p 6≡ q Wenn von zwei Zuständen ein a-Übergang zu zwei nicht-äquivalenten Zuständen existiert, dann sind auch die beiden Zustände nicht äquivalent. (p, a, r) ∈ δ ∧ (q, a, r 0 ) ∈ δ ∧ r 6≡ r 0 ⇒ p 6≡ q Etwas detaillierter lässt sich der Algorithmus so beschreiben: 1. Stelle eine Tabelle aller Zustandspaare {q, q 0 } mit q 6= q 0 von A auf. 2. Markiere alle Paare {q, q 0 } mit q ∈ F und q 0 ∈ / F (oder umgekehrt). 3. Für jedes noch unmarkierte Paar {q, q 0 } und jedes a ∈ Σ teste, ob {δ(q, a), δ(q 0 , a)} bereits markiert ist. Falls ja, wird auch {q, q 0 } markiert. 4. Der letzte Schritt wird so lange wiederholt, bis sich keine Änderungen mehr ergeben. 5. Alle jetzt noch unmarkierten Paar sind äquivalent und können daher verschmolzen werden. 10.3 10.3.1 Chomsky 2 Welchen Typ hat die Sprache ai+1 b3i , i ≥ 0? Geben Sie einen Automaten dazu an. Diese Sprache ist kontextfrei, da die Anzahl der b’s von der der a’s abhängt. Der Kellerautomat liest erstmal ein a ohne den Keller zu verändern, dann pusht er für jedes gelesene a drei Zeichen. Beim Lesen eines b’s wird jeweils eins dieser Zeichen vom Keller geholt. Akzeptanz durch leeren Keller. 10.3.2 Was ist eine kontextfreie Sprache? Eine kontextfreie Sprache ist vom Chomsky Typ 2, wird von einem Kellerautomaten akzeptiert und von einer kontextfreien Grammatik erzeugt. 10.3.3 Welche Automaten erkennen kontextfreie Sprachen? Kellerautomaten. 10.3.4 Erkennt ein deterministischer Kellerautomat kontextfreie Sprachen? Ein deterministischer Kellerautomat erkennt nur deterministisch-kontextfreie Sprachen. 10.3.5 Welche Normalformen für kontextfreie Sprachen gibt es und für was sind sie gut? Es gibt die Chomsky Normalform. Hier haben alle Produktionen die Form A → BC oder A → a. Die Chomsky Normalform wird für die Anwendung des CYK-Algorithmus’ gebraucht. Greibach Normalform. Alle Produktionen haben die Form A → aB1 B2 . . . Bn . Sie wird zur Konstruktion von Kellerautomaten benötigt. 136 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.3. Chomsky 2 10.3.6 Erklären und beweisen Sie das Pumping-Lemma für kontextfreie Sprachen. Sei L eine kontextfreie Sprache. Dann gibt es eine Zahl n ∈ N, so dass sich alle Wörter z ∈ L mit |z| ≥ n zerlegen lassen in z = uvwxy mit folgenden Eigenschaften: 1. |vx| ≥ 1 2. |vwx| ≤ n 3. für alle i ≥ 0 gilt: uv i wxi y ∈ L Beweisidee: Wenn eine Grammatik eine Sprache L mit unendlich vielen Wörtern erzeugt, dann muss es Wörter geben in deren Ableitungsbaum auf einem Pfad dieselbe Variable mehrfach vorkommt (Schubfachprinzip). S (1) S A A u v w A (1) A (2) (2) A x y u v v w x x y Die Abbildung zeigt einen solchen Ableitungsbaum für das Wort uvwxy und uvvwxxy. Man kann den bei Knoten (1) beginnenden Teilbaum so wählen, dass auf keinem Pfad dieses Teilbaums zwei gleiche Variablen vorkommen. Jetzt kann man das Teilstück des Baumes zwischen (1) und (2) beliebig oft wiederholen und erhält damit Ableitungsbäume für die Wörter uv i wxi y. Da in jedem bei (1) beginnenden Teilbaum auf keinem Pfad Variablen doppelt vorkommen gilt |vwx| ≤ n für ein geeignetes n. Man kann davon ausgehen, dass es in der Grammatik keine -Produktionen gibt, daher gilt auch |vx| ≥ 1. 10.3.7 Zeigen Sie, dass L = {am bm cm } nicht kontextfrei ist. Man nimmt an, dass L kontextfrei ist und für das mit dem Pumping Lemma zum Widerspruch. Man wählt das Wort z = an bn cn . Dann hat z die Länge 3n ≥ n. Wegen Bedingung 2 können v und x entweder nur aus a’s und b’s oder aus b’s und c’s bestehen. vx muss wegen Bedingung 1 mindestens ein Zeichen enthalten. Dann kann aber uv 2 wx2 y nicht in L sein, da es entweder mehr a’s als c’s oder mehr c’s als a’s enthält. 10.3.8 Was sind kontextfreie Grammatiken? Bei kontextfreie Grammatiken gilt für jede Produktion l → r, |l| ≤ |r und das l eine einzige Variable sein muss. 10.3.9 In welcher Klasse liegt die Sprache der Palindrome? Palindrom sind vom Chomsky-Typ 2. Sie sind nicht deterministisch-kontextfrei. 10.3.10 Wie funktioniert der Kellerautomat für Palindrome? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 137 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit Der Kellerautomat für Palindrome muss die Mitte des Wortes erraten. Der Kellerautomat könnte so aussehen. Q = {q0 , q1 } Γ = Σ ∪ {c} Z0 = c F =∅ δ = {((q0 , x, y), (q0 , xy)) | x ∈ Σ, y ∈ Γ} ∪ {((q0 , , y), (q1 , y) | y ∈ Γ} ∪ nächstes Eingabezeichen pushen (1) Entscheidung Mitte (gerade länge) (2) {((q0 , x, y), (q1 , y)) | x ∈ Σ, y ∈ Γ} ∪ {((q1 , x, x), (q1 , )) | x ∈ Σ} ∪ {((q1 , , c), (q1 , ))} Entscheidung Mitte (ungerade länge) (3) Keller mit Eingabe vergleichen (4) Startsymbol vom Keller löschen (5) Die Menge der Endzustände F ist leer, da der Automat durch den leeren Keller akzeptiert. 10.3.11 Was sind nutzlose Variablen? Eine Variable heisst nützlich wenn sie in mindestens einer Ableitung eines Wortes vorkommt. Wenn eine Variable nicht nützlich ist, ist sie nutzlos. Eine nützliche Variable ist produktiv und erreichbar (aber nicht umgekehrt). Eine Variable A heisst produktiv, wenn aus ihr mindestens ein Wort abgeleitet werden kann. Eine Variable A heisst erreichbar, wenn sie in mindestens einer Satzform vorkommt. 10.3.12 Was ist eine nullierbare Variable? Eine Variable A einer Grammatik G heisst nullierbar, wenn gilt A ⇒∗G . 10.3.13 Wie erstellt man eine Grammatik, die keine -Produktion, keine Kettenproduktion und keine nutzlosen Variablen enthält? 1. Elimination von -Produktion Wenn die Grammatik eine Produktion enthält mit der Form A → A1 . . . An bei der für ein i ∈ {1, . . . n} die Variable Ai nullierbar ist, dann wird die Produktion durch A → A1 . . . Ai−1 Ai+1 ersetzt (Ai bleibt also weg). Die wird solange angewandt, bis es keine solchen Produktionen mehr gibt. Produktionen der Form A → werden gelöscht. Diese Grammatik kann keine Sprachen erzeugen, die enthalten. 2. Elimination von Kettenproduktionen Jede Sequenz von Kettenproduktionen A ⇒∗G B gefolgt von eine Nicht-Kettenproduktion B → α wird ersetzt durch eine Produktion A → α. 3. Entfernen von nutzlosen Variablen (dieser Schritt ist bei manchen Definitionen der Chomsky Normalform nicht gefordert) Bei den beiden folgenden Schritten kommt es auf die Reihenfolge an. a) Alle nicht-produktiven Variablen und die Produktionen in denen sie vorkommen können entfernt werden, ohne dass die Sprache sich ändert. b) Alle nicht-erreichbaren Variablen und die Produktionen in denen sie vorkommen können entfernt werden, ohne dass die Sprache sich ändert. 10.3.14 Erklären Sie die Chomsky-Normalform und wie man sie erstellt. Eine Grammatik ist in Chomsky-Normalform, wenn alle Produktionen die Form A → BC oder A→a haben. Um eine Grammatik vom Typ 2, die keine -Produktion, keine Kettenproduktion und keine nutzlosen Variablen enthält, in Chomsky-Normalform zu überführen sind folgende Schritte notwendig. 138 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.3. Chomsky 2 1. Für jedes Terminal a wir eine neue Variable Ba und eine Produktion Ba → a eingefügt. Ausserdem ersetzt man in jeder Produktion der Form A → α mit |α| ≥ 2 jedes Zeichen a in α durch die Variable Ba 2. Jetzt ersetzt man jede Produktion der Form A → A1 . . . An mit n > 2 durch diese n−1 Produktionen A → A 1 C1 C1 → A 2 C2 C2 → A 3 C3 .. . Cn−2 → An−1 An wobei C1 , . . . , Cn−2 neue Variablen sind. Bei einer Grammatik in Chomsky Normalform hat die Ableitung eines Wortes der Länge n genau 2n − 1 Schritte. 10.3.15 Sind kontextfreie Sprachen unter Schnitt abgeschlossen? Nein. Beweis durch Gegenbeispiel. Die Sprachen L1 = {ai bj cj | i, j ≥ 0} und L2 = {ai bi cj | i, j ≥ 0} sind beide Kontextfrei. Der Schnitt der Sprachen L = L1 ∪ L2 = {ai bi ci | i ≥ 0} ist aber nicht kontextfrei. 10.3.16 Wenn ich Ihnen einen Automaten auf den Tisch stelle, können Sie dann sagen, welchen Typ von Sprachen er erkennt? Es muss sich um einen EA handeln, da der unendliche Keller des Kellerautomaten nicht auf den Tisch passen würde. 10.3.17 Wie sind Kellerautomaten definiert? Ein Kellerautomat über einem Alphabet Σ besteht aus einer endlichen Menge von Zuständen Q einem Startzustand z0 ∈ Q einem Kelleralphabet Γ einem Anfangssymbol Z0 ∈ Γ einem Menge von Endzuständen F einer Übergangsrelation δ ⊆ (Q × (Σ ∪ {}) × Γ) × (Q × Γ∗ ) 10.3.18 Wie definiert man einen Zustandsübergang? Zustandsübergang kann als Funktion oder Relation definiert werden. Definition mit Relation siehe 10.3.17. 10.3.19 Welchen Arten von Akzeptanz gibt es bei Kellerautomaten. Zeigen Sie die Äquivalenz dieser Arten. Es gibt Akzeptanz über Endzustände und Akzeptanz mit leerem Keller. Ein Automat A der durch leeren Keller akzeptiert, kann wie folgt in einen Automaten A 0 , der durch Endzustand akzeptiert umgewandelt werden. 1. Hinzufügen eines neuen Kellersymbols X0 ∈ /Γ 2. Hinzufügen eines Endzustandes Qf ∈ /Q 3. Automaten so modifizieren, dass er im ersten Schritt das Kellersymbol Z 0 durch Z0 X0 ersetzt 4. Automat A0 simuliert dann den Automaten A DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 139 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit 5. Wenn die Berechnung mit leerem eigentlich beendet wäre, liegt auf dem Keller noch das Symbol X0 . Deshalb werden Übergänge hinzugefügt, so dass der Automat X0 aus dem Keller löscht und in den Endzustand Qf übergeht. Ein Automat A der durch Endzustände akzeptiert, kann wie folgt in einen Automaten A0 , der durch leeren Keller akzeptiert umgewandelt werden. 1. Automat A0 simuliert Automat A 2. Wenn A0 einen Endzustand erreicht, kann er sich nicht-deterministisch dafür entscheiden, den Keller komplett zu löschen und damit Akzeptanz über leeren Keller zu erreichen. Problem: Wenn A0 den leeren Keller erreicht, A sich aber nicht in einem Endzustand befindet, wird A 0 das Wort akzeptieren, obwohl A es nicht tut. Lösung: Im ersten Schritt erzeugt A0 aus dem Startsymbol Z0 den Kellerinhalt Z0 X0 mit einem Kellersymbol X0 ∈ / Γ. Damit kann der Keller garantiert nie leer werden. Der Keller wird nur leer, wenn sich der Automat in einem Endzustand nicht-deterministisch für das komplette löschen des Kellers entscheidet. 10.3.20 Geben Sie die Grammatik für Palindrome an. Für das Alphabet Σ = {0, 1}. S S S S S S → → 0S0 1S1 → → 00 11 → → 0 1 10.3.21 Was ist eine linksrekursive Grammatik? Eine linksrekursive Grammatik enthält Produktionen bei denen die erste Variabel auf der rechten Seiten gleich der Variablen auf der linken Seite ist. Zum Beispiel A → AB Linksrekursive Grammatiken sind ungeeignet für die Verarbeitung mit Top-Down-Parsern, da diese dabei in Endlosschleifen geraten können. Für die Elimination von Linksrekursionen, siehe 2.3.6. 10.3.22 Was ist eine mehrdeutige Grammatik? Bei einer mehrdeutigen Grammatik gibt es für mindestens ein Wort verschiedene Ableitungsbäume. 10.3.23 Erklären Sie die Begriffe LL(k) und LR(k). siehe 2.3.3. i Welche Komplexität hat das Wortproblem? 10.3.24 Das Wortproblem kann mit dem CYK-Algorithmus in O(n3 ) gelöst werden. 10.3.25 Wie kann man eine Grammatik -frei machen? siehe 10.3.13 140 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.4. Chomsky 1 10.4 10.4.1 Chomsky 1 i Ist das Wortproblem für kontextsensitive Sprachen entscheidbar? Ja, aber N P -hart. 10.4.2 i Geben Sie einen Algorithmus für das Wortproblem an. Man startet mit dem Startsymbol und leitet so oft ab, bis man das Wort gefunden hat oder nicht mehr ableiten kann. n wie folgt definiert. Formell: Für m, n ∈ N werden die Mengen Tm n Tm = {w ∈ (V ∪ Σ)∗ | |w| ≤ n und w lässt sich aus S in höchstens m Schritten ableiten} n Die Mengen Tm , n ≥ 1 lassen sich induktiv über m definieren. T0n n Tm+1 Abln (X) = {S} n = Abln (Tm ), wobei = X ∪ {w ∈ (V ∪ Σ)∗ | |w| ≤ n und w 0 ⇒ w für ein w0 ∈ X} Abln (X) bildet also aus einer Menge von Wörtern (Satzformen) eine neue Menge, in dem sie X um alle Wörter, die in einem Schritt aus allen Wörter aus X herleitbar sind, erweitert. Aufgenommen werden nur Wörter, deren Länge ≤ n ist. Da es nur endlich viele Wörter der Länge n gibt, ist [ n Tm m≥0 für jedes n eine endliche Menge. Deshalb kann man folgenden Algorithmus angeben, der das Wortproblem für Typ 3, 2 und 1 Sprachen löst, da diese die Wortlängenmonotonie erfüllen. Als Eingabe erhält der Algorithmus ein Wort und eine Grammatik. Wortproblem(G, x) 1 n ← |x| 2 T ← {S} 3 do{ 4 T1 ← T 5 T ← Abln (T1 ) 6 } until (x ∈ T ) or (T = T1 ) 7 if (x ∈ T ) return Ja 8 else return Nein Dieser Algorithmus terminiert immer, hat aber exponentielle Laufzeit. 10.4.3 i Welche Komplexität hat dieser Algorithmus? Ist N P -hart und daher Komplexität O(2n ) 10.4.4 Was ist Wortlängenmonotonie? Auch bei kontextsensitiven Sprachen können Worte in jedem Ableitungsschritt nur länger werden oder gleich lang bleiben. Das ist eine grundlegende Voraussetzung für die Entscheidbarkeit des Wortproblems. 10.4.5 Welche Automaten erkennen kontextsensitive Sprachen? Linear beschränkte Turingmaschinen. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 141 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit 10.4.6 Was ist das für eine Sprache {ww|w ∈ {0, 1}∗ }? Es handelt sich um eine kontextsensitive Sprache. Diese Sprache beschreibt das aus dem Compilerbau bekannte Problem, das eine Variable zuerst deklariert werden muss, bevor sie benutzt wird. 10.4.7 Welchen Typ hat die Sprache ak bm ak bm , k ∈ N, m ≥ 0? Die Sprache ist kontextsensitiv. Sie entspricht dem Problem bei der Übersetzung eines Programmes festzustellen, ob die Anzahl der Parameter beim Methodenaufruf gleich der Anzahl der Parameter bei der Methodendeklaration ist. 10.5 10.5.1 Berechenbarkeit Warum ist die Ackermannfunktion nicht primitiv rekursiv? Informell: Man kann zeigen, dass man zur Berechnung von a(n+1, n+1) n verschachtelte For-Schleifen braucht. Da dies für beliebige n gilt, gibt es keine Obergrenze für die Anzahl der For-Schleifen. 10.5.2 i Was ist der Satz von Rice und was bedeutet er? Jede nicht-triviale Eigenschaft P ist unentscheidbar, d. h. die Sprache LP = {hM i | M ist eine Turing-Maschine mit P (L(M ))} ist nicht entscheidbar. Eine Eigenschaft von Sprachen ist eine Teilmenge P von aufzählbaren Sprachen. Man schreibt P (L) wenn L die Eigenschaft erfüllt, d.ḣ. L ∈ P . Man schreibt ¬P (L) wenn L sie nicht erfüllt. Eine Eigenschaft heisst nicht-trivial wenn es wenigstens eine aufzählbare Sprache mit L mit P (L) und wenigstens eine aufzählbare Sprache L0 mit ¬P (L0 ) gibt. Beispiel : Es ist nicht entscheidbar, ob eine Turing-Maschine nur endlich viele Wörter akzeptiert (d. h. ob die Sprache endlich ist). Die Eigenschaft P ist definiert durch P (L) = {L | |L| < ω}. Die Eigenschaft ist nicht trivial, da ¬P (Σ ∗ ) und P (∅). Es ist nicht entscheidbar, ob die akzeptierte Sprache regulär ist. P (L) = {L | L ist regulär}. Eigenschaft ist nicht-trivial: ¬P (an bn ) und P (∅) 10.5.3 i Erklären Sie den Begriff Aufzählbarkeit. Der Begriff Aufzählbarkeit ist mit dem der Abzählbarkeit eng verwandt: Eine unendliche Menge M ist aufzählbar wenn es eine surjektive, berechenbare Abbildung von N nach M gibt: M = {f (1), f (2), f (3), . . .} Die Abbildung muss auf jeden Fall berechenbar sein, hier liegt der Unterschied zur Abzählbarkeit. Wenn diese Abbildung berechenbar ist, so kann auch jedes Element der Menge M tatsächlich erzeugt werden. 10.5.4 Was heisst rekursiv aufzählbar ? Die Begriffe aufzählbar und rekursiv aufzählbar bedeuten im deutschen dasselbe. 10.5.5 Zeigen sie, dass eine rekursive aufzählbare Sprache semi-entscheidbar ist. Satz: Eine Sprache ist rekursiv aufzählbar genau dann, wenn sie semi-entscheidbar ist. Beweis: Von links nach rechts: Wenn die Sprache L mit der Funktion f rekursiv aufzählbar ist, dann muss man nur der Reihe nach i = 0, 1, 2 . . . in f einsetzen und testen ob f (n) = x ist. 142 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.5. Berechenbarkeit Von rechts nach links: Die Annahme ist, das die Sprache L semi-entscheidbar ist. Man sucht nach einer Funktion f deren Wertebereich L ist, die also alle Elemente von A aufzählt. Wenn L semi-entscheidbar ist, dann gibt es auch einen Algorithmus M , der diese Semi-Entscheidung berechnet. Der Algorithmus bekommt ein Wort x der Sprache L als Eingabe und antwortet nach einer endlichen Anzahl an Schritt mit ja oder er antwortet gar nicht. Mit Hilfe dieses Algorithmus kann der in der Abbildung dargestellte Automat die Sprache aufzählen, in dem er mit natürlichen Zahlen gefüttert wird (Automat nicht im Sinne von endlicher Automat). n C1 (x',y') M' x' C2 x M ja x y' K a {...} M ist der bereits beschriebene Semi-Entscheidungs-Algorithmus. Er ist der zentrale Bestandteil des Automaten M 0 . M 0 erweitert M um eine Kontrolleinheit K, die M nach einer bestimmten Anzahl von Schritten abbrechen kann. Des weiteren wählt man a als ein Wort, das in der Sprache L liegt. Jetzt erhält der Automat ein 2-Tupel (x0 , y 0 ) wobei x0 ∈ N und y 0 ∈ N als Eingabe. y 0 wird der Kontrolleinheit K zugeführt, diese kann M nach y 0 Schritten abbrechen. Der andere Teil des Tupels x0 ist ebenfalls eine natürliche Zahl, die aber ein Wort codiert. Entscheidend dabei ist, dass jedes Wort als natürliche Zahl codiert werden kann. Dieses codierte Wort x0 wird von Codierer C2 in das Wort x übersetzt. Der Algorithmus wird nun auf das Wort x angesetzt und gibt, falls x ein Wort der Sprache ist, ja aus. Falls M ja ausgibt, gibt M 0 das Wort x aus. Da nicht entscheidbar ist, ob der Algorithmus jemals anhält, bricht ihn die Kontrolleinheit nach y 0 Schritten ab. In diesem Falle gibt M 0 das Wort a aus. Man sieht, dass M 0 auf jeden Fall stoppt und immer ein Wort der Sprache ausgibt. M 0 erzeugt alle Wörter der Sprach, wenn die Eingabe-Tupel alle Wörter enthalten und für jedes Wort, die Anzahl der Schritte y 0 gross genug ist. Um dies sicher zustellen muss man den Algorithmus einfach mit allen Wörter, die es gibt füttern und für jedes Wort alle möglichen Anzahlen an Schritten zulassen. Da heisst, M 0 erzeugt alle Wörter, wenn er als Eingabe alle Tupel (N × N) erhält. Genau dafür ist der Codierer C1 zuständig. Dieser erhält als Eingabe eine natürliche Zahl und erzeugt als Ausgabe ein 2-Tupel mit natürlichen Zahlen. Es gibt berechenbare Funktionen, die solche Tupel erzeugen ([Schöning, 1997, S. 111]), da (N × N) aufzählbar ist. Der ganze Automat erzeugt nun alle Wörter der Sprache L, wenn er alle natürlichen Zahlen als Eingabe enthält. Er berechnet also eine Funktion mit Definitionsbereich N und Wertebereich L. 10.5.6 Erklären Sie den Begriff Berechenbarkeit. Laut der Churchschen These stimmt die durch die formale Definition der Turing-Berechenbarkeit erfasste Klasse von Funktionen genau mit der Klasse der im intuitiven Sinne berechenbaren Funktionen überein. Informell: Berechenbar ist, was man z. B. in Java programmieren kann. 10.5.7 Wie sind die primitiv rekursiven Funktionen definiert? Die primitiv rekursiven Funktionen sind induktiv definiert: 1. Alle konstanten Funktionen sind primitiv rekursiv. 2. Alle identischen Abbildungen (Projektionen) sind primitiv rekursiv. 3. Die Nachfolgerfunktion s(n) = n + 1 auf den natürlichen Zahlen ist primitiv rekursiv. 4. Jede Funktion die durch Einsetzung (Komposition) von primitiv rekursiven Funktionen entsteht, ist selbst primitiv rekursiv. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 143 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit 5. Jede Funktion, die durch sog. primitive Rekursion aus primitiv rekursiven Funktionen entsteht, ist primitiv rekursiv. Primitive Rekursion bedeutet, dass die Definition von f (n + 1, . . .) zurückgeführt wird auf f (n, . . .). Formal muss f ein Gleichungssystem der folgenden Form erfüllen: f (0, . . .) = g(. . .) f (n + 1, . . .) = h(f (n, . . .), . . .) wobei g, h selbst primitiv rekursive Funktionen sind. 10.5.8 Wie ist der µ-Operator definiert? Durch Anwendung des µ-Operators wird aus einer n + 1-stelligen Funktion g eine n-stellige Funktion µg. min{k ∈ N | g(x, k) = 0 ∧ (g(x, j) ist definiert für alle j ≤ k)} (µg)(x) = undefiniert, falls min nicht existiert µg(x) bezeichnet also den kleinsten Index k, für den g(x, k) = 0 gilt und für den für alle j ≤ k der Wert g(x, j) definiert ist. 10.5.9 Wie unterscheiden sich die primitiv rekursiven von den µ-rekursiven Funktionen? Die Berechbarkeitsmächtigkeit der primitiv rekursiven Funktion entspricht der Loop-Berechenbarkeit. Die Berechbarkeitsmächtigkeit der µ-rekursiven Funktionen entspricht der While- bzw. Turing-Berechenbarkeit. 10.5.10 Wie geht die Umwandlung einer nicht-deterministischen Turingmaschine in eine Deterministische? Dazu muss erstmal die Idee der beschränkten Simulation beschrieben werden. Ein beschränkter Simulator Sim(M ) einer TM M erweitert M um einen Zähler der mit n initialisiert wird. Sim(M ) erhält als Eingabewort w#n wobei w das Eingabewort und n ein Wort aus n Strichen ist. Bei jedem Abarbeitungsschritt wird der Zähler um 1 decrementiert (ein Strich gelöscht). Ist der Zähler bei 0 bevor das Wort w erkannt wurde, akzeptiert die Maschine nicht. Diese Idee kann nun erweitert werden um eine DTM zu bauen. Gegeben sei folgende nicht-deterministische Turing-Maschine M = (Q, Σ, Γ, δ, q0 , B, F ). Für einen Zustand q ∈ Q und ein gelesenes Zeichen a definiert man A(q,a) = {(p, b, m) | ((q, a), (p, b, m) ∈ δ} als die Menge der möglichen Übergänge nach q bei gelesenem Eingabezeichen a. Man nennt A(q,a) die Menge der Alternative. Die maximale Anzahl k von Alternativen der Turingmaschine ist k = max{|A(q,a) | | q ∈ Q, a ∈ Γ} Nun kann man jeder Alternative aus A(q,a) eindeutig eine Nummer aus {1, 2, . . . , k} zuordnen. Jetzt sei π = k1 k2 . . . kn mit ki = {1, 2, . . . , k}. Sei Sim(M ) eine Turingmaschine, die die Eingabe w#π erhält, wobei w ∈ Σ∗ . Sim(M ) simuliert die Turingmaschine M auf der Eingabe w für |π| Schritte; ki gibt in jedem Simulationsschritt an, welche Alternative der TM M die TM Sim(M ) wählen soll. Wenn die Wahl einer alternative nicht möglich ist, wird die Simulation erfolglos abgebrochen. Diese Maschine ist deterministisch, da jede Alternativenwahl vorgeben ist und stoppt nach |π| Schritten. Nun kann man aus Sim(M ) eine Maschine M 0 bauen, die für die Eingabe w systematisch alle Paare w#π erzeugt und dann Sim(M ) mit Eingabe w#π startet. Wenn Sim(M ) für ein π die Eingabe w#π akzeptiert, dann akzeptiert M 0 die Eingabe w. Es gilt L(M ) = L(M 0 ). Denn es gilt w ∈ L(M 0 ) gdw. ein π ∈ {1, 2, . . . , k}∗ mit w#π von Sim(M ) akzeptiert wird gdw w ∈ L(M ) gilt. 10.5.11 Zeigen Sie die Unentscheidbarkeit des Wortproblems? Definition des Wortproblems: LW = {hM i#hwi | M ist eine Turing-Maschine und w ∈ L(M )} wobei hM i und hwi geeignete Repräsentationen der Turing-Maschine bzw. eines Wortes sind. Die Frage ist nun, ist diese Sprache entscheidbar, das heisst, gibt es eine Turing-Maschine, die genau diese Sprache erkennt, also für jede Kombination Turing-Maschine–Sprache mit ja oder nein antworten kann. 144 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.5. Berechenbarkeit Die Unentscheidbarkeit wird durch einen Widerspruchsbeweis gezeigt. Man nimmt an, dass es eine deterministische Turing-Maschine W mit L(W ) = LW gibt, die für alle Eingaben hält, also das Wortproblem löst. Aus dieser angenommenen Turing-Maschine W wird jetzt eine Turing-Maschine M , wie in der Abbildung gezeigt, konstruiert. ja M x D x#x W ja nein nein Ein Wort x, das in die Turing-Maschine M gegeben wird, wird zuerst vom Duplizierer D verdoppelt zum dem Wort x#x. Diese Wort wird nun in die Turing-Maschine W gegeben, die auf jedem Wort hält. Zum Schluss wird das Ergebnis noch negiert. Nun gibt man der Turing-Maschine ihre eigene Repräsentation als Eingabe, also x = hM i. M verhält sich dann wie folgt. Auf jeden Fall hält M , da der Duplizierer D immer hält und die Maschine W laut der Widerspruchsannahme auch immer hält. Wenn M mit ja antwortet, dann muss W auf der Eingabe hM i#hM i mit nein gehalten haben. Gemäss der Widerspruchsannahme gilt dann hM i#hM i ∈ / LW . Die Turing-Maschine W löst ja das Wortproblem, das sie mit nein Antwortet ist also hM i kein Wort der Sprache L(M ), die durch die Turing-Maschine M erkannt wird. Es gilt also hM i ∈ / L(M ). Das heisst aber wiederum, dass die Maschine M auf der Eingabe hM i nicht mit der Antwort ja hält. Dieser Widerspruch zeigt, dass die Turing-Maschine M für die Eingabe hM i nicht mit der Antwort ja halten kann. Wenn M auf der Eingabe hM i mit der Antwort nein hält, dann muss W auf der Eingabe hM i#hM i mit ja halten. Dann gilt laut der Widerspruchsannahme, dass hM i#hM i ∈ LW . Aufgrund der Definition von LW gilt dann aber auch hM i ∈ L(M ). Was aber bedeutet, dass M mit der Antwort ja hält. Dieser Widerspruch zeigt, dass die Turing-Maschine M für die Eingabe hM i nicht mit der Antwort nein halten kann. Die Turing-Maschine M hält also für die Eingabe hM i weder mit ja noch mit nein . Da die Maschine aber per Konstruktion immer hält, liegt ein Widerspruch vor. Die einzige Annahme die getroffen wurde, war, dass es eine Maschine W gibt, die das Wortproblem löst. Aufgrund des Widerspruchs kann W nicht existieren und somit ist das Wortproblem nicht entscheidbar. 10.5.12 Beweisen Sie die Unentscheidbarkeit des Halteproblems. Definition des Halteproblems: LH = {hM i#hwi | M ist eine Turing-Maschine und hält bei der Eingabe w} wobei hM i und hwi geeignete Repräsentationen der Turing-Maschine bzw. eines Wortes sind. Die Frage ist nun, ist diese Sprache entscheidbar, das heisst, gibt es eine Turing-Maschine, die genau diese Sprache erkennt, also für jede Kombination Turing-Maschine–Sprache mit ja oder nein antworten kann. Man zeigt die Unentscheidbarkeit des Halteproblems indem man das Wortproblem auf das Halteproblem reduziert. Man zeigt, also, dass man aus einer Maschine die das Halteproblem entscheidet, eine Maschine bauen könnte, die das Wortproblem entscheidet. Da das Wortproblem nicht entscheidbar ist, kann es ein solche Maschine nicht geben. Eine deterministische Turing-Maschine M kann man so umbauen, dass sei ein Wort genau dann akzepf bezeichnet. tiert, wenn sie auf dem Wort hält. Die umgebaute Maschine wird mit M Die folgende Maschine entscheidet das Wortproblem. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 145 Kapitel 10. Automaten, Formale Sprachen und Berechenbarkeit ja 〈M〉#〈w〉 〈M〉#〈w〉 U ja H nein nein Sie wird mit einer Instanz des Wortproblems gefüttert. Die Umbauer-Maschine U baut die Maschine M f um. Diese Maschine akzeptiert w wenn sie auf ihm hält. Jetzt wird eine Beschreiin eine Maschine M bung der umgebauten Maschine und das Wort in die Maschine H eingeführt. H ist die hypothetische f auf w hält und antwortet immer. Maschine, die das Halteproblem löst. Sie testet nun ob, M Wie man sieht, würde dies Maschine das Wortproblem lösen. Da das Wortproblem aber bewiesenermassen unentscheidbar ist, kann die Maschine H nicht existieren und somit das Halteproblem auch nicht entscheidbar. i Beweisen Sie die das Halteproblem. (2. Variante) 10.5.13 Der Beweis läuft in zwei Schritten, zuerst beweist man das spezielle Halteproblem und reduziert es dann auf das Halteproblem. Spezielles Halteproblem: Unter dem speziellen Halteproblem versteht man die Sprache K = {w ∈ {0, 1}∗ | Mw angesetzt auf w hält} Diese Sprache ist nicht entscheidbar. Beweis: Man nimmt an, dass K entscheidbar ist und führt diese Annahme zum Widerspruch. Falls K entscheidbar ist, dann lässt sich χK mit einer Turingmaschine M berechnen. Nun baut man diese Turing-Maschine M um in eine Turingmaschine um in eine Turingmaschine M 0 , die wie folgt definiert ist. M' start M =0? nein ja stop Die Maschine M 0 stoppt wenn M 0 ausgibt, ansonsten gerät M 0 in eine Endlosschleife. Jetzt betrachtet man ein Codewort w 0 der Maschine M 0 und kommt zu folgenden Schlussfolgerungen: M 0 angesetzt auf w 0 hält M 0 angesetzt auf w 0 hält ⇔ ⇔ ⇔ ⇔ ⇔ M angesetzt auf w 0 gibt 0 aus χK (w0 ) = 0 w0 ∈ /K Mw0 angesetzt auf w 0 hält nicht M 0 angesetzt auf w 0 hält nicht Der Widerspruch zeigt, dass die Annahme falsch war, K ist nicht entscheidbar. Allgemeines Halteproblem: Unter dem allgemeinen Halteproblem versteht man die Sprache H = {w#x| Mw angesetzt auf x hält} Diese Sprache ist nicht entscheidbar. Beweis: Wir wissen bereits, dass das spezielle Halteproblem nicht entscheidbar ist, dieses reduzieren wir jetzt auf das allgemeine Halteproblem. D. h. wir zeigen, dass das spezielle Halteproblem eine Spezialfall des allgemeinen Halteproblems ist, dann kann das allgemeine Halteproblem nicht entscheidbar sein. Formal: Man zeigt, das K ≤ H. Dazu wählt man als Reduzierungsfunktion f (w) = w#w. Dann gilt: w ∈ K ⇔ f (w) ∈ H. 146 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 10.5. Berechenbarkeit i Was ist die Kleen’sche Normalform? 10.5.14 Ein Programm, dass nur aus einer einzigen While-Schleife besteht, ist in Kleen’scher Normalform. 10.5.15 Gibt es Funktionen, die Turing aber nicht Loop-berechenbar sind? Ja. Z. B. die Ackermannfunktion. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 147 Kapitel 11 Logik 11.1 11.1.1 Allgemeines Was bedeuteten Korrektheit und Vollständigkeit? Die Aufgabe eines Kalküls ist es die Unerfüllbarkeit einer Formelmenge zu beweisen. Das Kalkül muss vollständig und korrekt sein. Korrektheit bedeutet, dass keine erfüllbare Formel vermeintlich als unerfüllbar erkannt wird. Vollständigkeit bedeutet, dass jede unerfüllbare Formel nachgewiesen werden muss. 11.2 11.2.1 Aussagenlogik Erklären Sie was gültige, erfüllbare und unerfüllbare Formeln sind. Eine Formel F ist erfüllbar falls sie mindestens ein Modell A hat. Man schreibt A |= F . Eine Formel F heisst gültig falls für jede zu F passende Belegung A gilt A |= F . Man nennt F eine Tautologie und schreibt |= F . Eine Formel F heisst unerfüllbar falls es keine passende Belegung, die ein Modell ist, gibt. 11.2.2 Wenn Sie einen Entscheidungsalgorithmus für die Erfüllbarkeit haben, wie können Sie zeigen, dass zwei Formeln äquivalent sind? Zeigen, dass A ↔ B eine Tautologie ist. 11.2.3 i Wie funktioniert die aussagenlogische Resolution? Die Resolution ist ein aussagenlogisches Kalkül. Um Resolution anzuwenden muss die Formel in KNF dargestellt werden. Vorteilhaft ist es dann die KNF-Formel als Mengen sog. Klauseln darzustellen. Aus F = (L1,1 ∨ . . . ∨ L1,n1 ) ∧ . . . ∧ (Lk,1 ∨ . . . ∨ Lk,nk ) wird F = {{L1,1 , . . . , L1,n1 }, . . . , {Lk,1 , . . . , Lk,nk }} wobei L Literale sind. Wenn K1 , K2 und R Klauseln sind, dann heisst R Resolvent von K1 und K2 , falls ein Literal L gibt mit L ∈ K1 und L ∈ K2 und R diese Form hat R = (K1 − {L}) ∪ (L2 − {L}) Wenn F eine Klauselmenge ist, dann ist Res(F ) definiert als Res(F ) = F ∪ {R | R ist Resolvent zweier Klauseln aus F } DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 149 Kapitel 11. Logik Desweiteren ist Res0 (F ) = F und Ausserdem ist Res∗ (F ) = Resn+1 (F ) = Res(Resn (F )) [ Resn (F ) n≥0 Auf Unerfüllbarkeit wird mit dem Resolutionssatz getestet: Resolutionssatz: Eine Klauselmenge F ist unerfüllbar genau dann, wenn 2 ∈ Res∗ (F ). 11.2.4 Was ist das Resolutionslemma? Sei F eine Formel in KNF, dargestellt als Klauselmenge. Ferner sei R ein Resolvent zweier Klauseln K1 und K2 . Dann sind F und F ∪ {R} äquivalent. Beweis: Sei A eine zu F und damit auch zu F ∪ {R} passende Belegung. Wenn A |= F ∪ {R}, dann gilt natürlich auch A |= F . Umgekehrt wird angenommen, dass A |= F , dann gilt für alle Klauseln K ∈ F : A |= K. Der Resolvent R hat die Form R = (K1 − {L}) ∪ (K2 − {L}) mit K1 , K2 ∈ F und L ∈ K1 , L ∈ K2 . Jetzt muss man zwei Fälle unterscheiden. 1. Fall : A |= L Da A |= L gilt sicherlich A 6|= L. Weiterhin gilt A |= K2 . Wenn A Modell für K2 ist, dann gilt auch A |= (K2 −{L}, da das Literal, das aus der Klausel entfernt wird, ja eh nicht erfüllbar ist. Innerhalb der Klausel sind alle Literale mit ∨ verbunden, deshalb ändert das entfernen einer 0 nichts. Der Resolvent wird durch Vereinigung von (K1 − {L}) und (K2 − {L}) gebildet. Die einzelnen Literale in R sind also wieder alle durch ∨ verbunden, da A |= K2 gilt, muss auch A |= R gelten. 2. Fall : A 6|= L Da A |= k1 , gilt auch A |= (K1 − {L}) und somit A |= R. Es gibt nur diesen beiden Fälle und für beide gilt, A |= R, also muss auch A |= F ∪ {R} gelten. 11.2.5 i Beweis für Korrektheit und Vollständigkeit der Resolution. Resolutionssatz: Eine Klauselmenge F ist unerfüllbar genau dann, wenn 2 ∈ Res∗ (F ). Korrektheit: Angenommen 2 ∈ Res∗ (F ). Die leere Klausel 2 kann nur durch Resolution zweier Klauseln K1 und K2 mit K1 = {L} und K2 = {L} entstanden sein. Aus dem Resolutionslemma folgt: F ≡ Res1 (F ) ≡ Res2 (F ) ≡ . . . ≡ Resn (F ) ≡ . . . Da 2 in Res∗ (F ) enthalten ist (Annahme), ist für ein n ≥ 0, 2 ∈ Resn (F ), und damit auch K1 , K2 ∈ Resn (F ). Das es kein Modell gibt, das sowohl K1 als auch K2 erfüllt, ist Resn (F ) unerfüllbar. Da Resn (F ) ≡ F , ist F unerfüllbar. Vollständigkeit: Die Annahme ist nun, dass F unerfüllbar ist und man will zeigen, dass in diesem Falle auch tatsächlich 2 ∈ Res∗ (F ) gilt. Der Beweis ist ein Induktionsbeweis über die Anzahl n der atomaren Formeln in F . Aufgrund des Endlichkeitssatzes kann man sich auch wenn F eine unendliche Formelmenge ist, auf eine endliche unerfüllbare Teilmenge von F beschränken. Induktionsanfang: Falls n = 0, so kann F nur F = {2} sein und somit ist 2 ∈ Res ∗ (F ). Induktionsschritt: Man wählt ein beliebiges aber festes n. Es wird angenommen, dass für jede unerfüllbare Klauselmenge G, die nur die atomaren Formeln A1 , A2 , . . . An enthält, gilt 2 ∈ Res∗ (F ). Jetzt sei F eine Klauselmenge mit den atomaren Formeln A1 , A2 , . . . An+1 . Jetzt werden aus F zwei neue Klauselmengen F1 und F0 wie folgt konstruiert. F0 : Das Atom An+1 wird auf den Wert 0 fixiert. Dann entsteht F0 aus F indem jedes Vorkommen von An+1 aus den Klausel gestrichen wird. Das geht da, A ∨ 0 = A. Ausserdem wird jede Klausel, die ¬An+1 enthält komplett gestrichen. Eine Klausel, die den Wert 1 enthält, hat insgesamt den Wert 1 und kann damit gestrichen werden. 150 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.2. Aussagenlogik F1 : Das Atom An+1 wird auf den Wert 1 fixiert. Dann entsteht F1 aus F indem jedes Vorkommen von ¬An+1 aus den Klausel gestrichen wird. Das geht da, A ∨ 0 = A. Ausserdem wird jede Klausel, die An+1 enthält komplett gestrichen. Eine Klausel, die den Wert 1 enthält, hat insgesamt den Wert 1 und kann damit gestrichen werden. Zu beachten ist, dass weder F0 noch F1 das Atom An+1 oder ¬An+1 enthalten. Im nächsten Schritt will man zeigen, dass sowohl F0 wie F1 unerfüllbar sind. Dies wird mit einem Widerspruchsbeweis erreicht (nur für F0 ausgeführt): Angenommen es gibt eine Belegung A : {A1 , . . . , An } → {0, 1}, die F0 erfüllt, dann ist A0 ein Modell für F , wobei A0 wie folgt definiert ist: A(B) falls B ∈ {A1 , . . . , An } 0 A (B) = 0 falls B = An+1 Die Existenz des Modells A0 steht aber im Widerspruch zur angenommenen Unerfüllbarkeit von F . Deshalb kann F0 nicht erfüllbar sein, aus dem selben Grund ist auch F1 unerfüllbar. Man weiss nun, dass F0 und F1 unerfüllbar sind, daher kann auf sie die Induktionsvoraussetzung angewandt werden und es gilt 2 ∈ Res∗ (F0 ) und 2 ∈ Res∗ (F1 ). Das heisst auch, dass es Klauseln K1 , . . . , Km geben muss mit: Km = 2 und für i = 1, . . . , m gilt: Ki ∈ F0 oder Ki ist Resolvent zweier Klauseln Ka , Kb mit a, b < i. D. h. es gibt für F0 eine Folge von Klausel K1 , . . . , Km , die zur leeren Klausel führt. Für F1 gibt es ebenfalls eine Folge K10 , . . . , Kt0 . Einige der Klauseln Ki entstanden aus F wobei das Vorkommen des Literals An+1 gestrichen wurde. Jetzt wird die ursprüngliche Klausel Ki ∪{An+1 } wieder hergestellt und An+1 bei den Resolutionsschritten mitgeführt. Es kann nur passieren, dass im Laufe der Resolutionsschritte die leere Klausel auftaucht, dann ist alles gezeigt. Wenn dies nicht passiert, ist am Ende auf jeden Fall {A n+1 } ∈ Res∗ (F ). Durch das Wiedereinfügen der von ¬An+1 in die Folge K10 , . . . , Kt0 , ergibt sich am Ende auf jeden Fall {¬An+1 } ∈ Res∗ (F ). Jetzt braucht nur noch ein Resolutionsschritt durchgeführte werden {An+1 } {¬An+1 } 2 und es gilt 2 ∈ Res∗ (F ). 11.2.6 i Woraus besteht die Aussagenlogik (Syntax/Semantik)? Syntax. Eine atomare Formel hat die Form Ai , wobei i = 1, 2 . . .. Formeln werden induktiv definiert. 1. Alle atomaren Formeln sind Formeln. 2. Für alle Formeln F und G sind (F ∧ G) und (F ∨ G) Formeln. 3. Für jede Formel F ist ¬F eine Formel. Semantik. Es gibt eine Funktion A, die die atomaren Formeln auf die Menge der Wahrheitswerte {0, 1} abbildet. Desweiteren gibt es eine Funktion  die Menge der Formeln auf die Wahrheitswerte abbildet. Die Unterscheidung zwischen A und  hat formale Gründe, da  einen grösseren Definitionsbereich als A hat. In einigen Bücher wird diese Unterscheidung nicht gemacht und in allen wird sie früher oder später weggelassen. Die Semantik ist wie folgt definiert. 1. für jede atomare 2. Â((F ∧ G)) = 3. Â((F ∨ G)) = Formel A ist Â(A) = A(A). 1, falls Â(F ) = 1 und Â(G) = 1 0, sonst 1, 0, falls Â(F ) = 1 oder Â(G) = 1 sonst DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 151 Kapitel 11. Logik 4. Â((¬F )) = 11.2.7 1, 0, falls Â(F ) = 0 sonst Was sind die minimalen Junktoren? Sowohl mit NAND als auch NOR können ∧, ∨, ¬ dargestellt werden. 11.2.8 Was ist eine Basis? Eine Menge O von Operatoren ist eine Basis (vollständig) falls es zu jeder Funktion f : W n → W, n ≥ 1 eine Formel F gibt, sodass F enthält nur Operatoren aus O und atomare Aussagen A1 , . . . An für alle zu F passenden Belegungen A gilt f (A(A1 ), . . . , A(An )) = A(F ). 11.2.9 Erklären Sie die Konjunktive Normalform. Eine Formel ist in konjunktiver Normalform, wenn sie folgende Form hat: F = (L1,1 ∨ . . . ∨ L1,n1 ) ∧ . . . ∧ (Lk,1 ∨ . . . ∨ Lk,nk ) Beweis dafür, dass es für jede Formel eine konjunktive Normalform gibt, ist ein Induktionsbeweis über den Formelaufbau. Algorithmus zum Erzeugen der KNF: 1. Ersetze in F jedes Vorkommen einer Teilformel der Bauart ¬¬G durch G ¬(G ∧ H) durch (¬G ∨ ¬H) ¬(G ∨ H) durch (¬G ∧ ¬H) bis keine derartige Teilformel mehr vorkommt. 2. Ersetze in F jedes Vorkommen einer Teilformel der Bauart (F ∨ (G ∧ H)) ((F ∧ G) ∨ H) durch durch ((F ∨ G) ∧ (F ∨ H)) ((F ∨ h) ∧ (G ∨ H)) bis keine derartige Teilformel mehr vorkommt. Die Formel ist nun in KNF. Es kann allerdings sein, dass noch überflüssige Disjunktion vorkommen. Diese jedoch zulässig, da es sich um Tautologien handelt. Beispiel: (¬A → B) ∧ ((A ∧ ¬C) → B) = (A ∨ B) ∧ (¬(A ∧ ¬C) ∨ B) = (A ∨ B) ∧ ((¬A ∨ C) ∨ B) = (A ∨ B) ∧ (¬A ∨ C ∨ B) 11.2.10 Erklären Sie die Disjunktive Normalform. Eine Formel ist in disjunktiver Normalform, wenn sie folgende Form hat: F = (L1,1 ∧ . . . ∧ L1,n1 ) ∨ . . . ∨ (Lk,1 ∧ . . . ∧ Lk,nk ) Beweis dafür, dass es für jede Formel eine disjunktive Normalform gibt, ist ein Induktionsbeweis über den Formelaufbau. 152 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.2. Aussagenlogik i Was ist der Endlichkeitssatz? 11.2.11 Eine Menge M von Formel ist erfüllbar genau dann, wenn jede der endlichen Teilmengen von M erfüllbar ist. i Beweis des Endlichkeitssatzes? 11.2.12 Die Richtung von links nach rechts ist einfach. Jedes Modell für M ist ein Modell für jede Teilmenge von M und damit auch für jede endliche Teilmenge. In die andere Richtung wird es kompliziert, daher zerlegt man den Beweis am Besten in mehrere Schritte. 1. Erst nochmal die Aufgabe verdeutlichen: Man nimmt an, jede endliche Teilmenge von M ist erfüllbar, dass heisst sie hat ein Modell. Die Aufgabe ist nun aus diesen unendlich vielen Modellen ein Modell für M zu konstruieren. Wenn das gelingt, ist auch die Richtung von rechts nach links bewiesen. 2. Für jedes n ≥ 1 sei Mn die Menge der Formel in M, die nur die atomaren Formeln A1 , . . . An enthalten. Jede dieser Mengen Mn ist wieder unendlich, da es unendlich viele Möglichkeiten gibt n n atomare Formeln zu einer Formel zusammen zubauen. Aber es gibt höchstens k ≤ 22 verschiedene Formeln in M, die nicht äquivalent zueinander sind. n Wieso? Für eine Formel mit n atomaren Formeln kann man höchstens 22 verschiedene Wahrheitstafeln aufstellen. Für jede atomare Formel zwei Möglichkeiten, daher 2n und dann noch pro n Belegung wahr oder ’falsch, macht 22 verschiedene Wahrheitstafeln. Man nennt diese nicht äquivalenten Formeln in Mn jetzt F1 . . . Fk , da es ja nicht mehr als k gibt. Jetzt kann man für jede Formel F aus Mn eine zu ihr äquivalente Formel Fi mit i ≤ k finden. Formell: Für jedes F ∈ Mn gibt es ein i ≤ k mit Fi ≡ F . Jedes Modell was Modell für die Formelmenge {F1 , . . . , Fk } ist, muss daher auch Modell für Mn sein. Das ist schon mal nicht schlecht, das Modell für die endliche Formelmenge {F1 , . . . , Fk } ist auch Modell für die unendliche Formelmenge Mn (damit ist leider noch nichts bewiesen). Das Modell für die Formelmenge Mn heisst ab jetzt An . Dieses Modell hat eine wichtige Eigenschaft. Da M1 ⊆ M2 ⊆ . . . ⊆ Mn , ist An nicht nur Modell für Mn , sondern auch für M1 , M2 , . . . , Mn−1 . 3. Eigentlich sucht man ja nach dem Modell A für die Menge M. Diese Modell muss nun aus den Modellen Ai zusammengesetzt (konstruiert) werden. Zur Schreibweise: Das gesuchte Modell A ist eine Funktion die von den atomaren Formeln A n auf die Wahrheitswerte {0, 1} abbildet. Diese Funktion kann auch in Relationenschreibweise geschrieben werden, wobei A(An ) = 1 durch (An , 1) und A(An ) = 0 durch (An , 0) ausgedrückt wird. Das hat den Vorteil das man die Mengenoperation ∪ zum Erweitern des Definitionsbereichs verwenden kann. Die Belegung B = {(A1 , 1)} ∪ {(A2 , 0)} z. B. weist dem Atom A1 1 und dem Atom A2 0 zu. Man gibt nun einen Algorithmus an, der das gesuchte Modell A konstruiert. Im letzten Schritt wird dann gezeigt, dass dieses Modell auch tatsächlich ein Modell für M ist. 4. Dieser Algorithmus baut das Modell A in mehreren Stufen auf. In der ersten Stufe wird A einfach auf die leere Menge gesetzt, enthält also für kein einziges Atom eine Belegung. Zusätzlich gibt es eine unendliche Indexmenge I, die zu Beginn auf I = {1, 2, . . .} gesetzt wird. 01 02 04 05 06 07 Stufe 0: A←∅ I ← {1, 2, 3, . . .} Stufe n > 0: if (es gibt unendlich viele Indizes i ∈ I mit Ai (An ) = 1) { DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 153 Kapitel 11. Logik 08 09 10 11 12 13 A ← A ∪ {(An , 1)} I ← I − {i | Ai (An ) 6= 1} } else { A ← A ∪ {(An , 0)} I ← I − {i | Ai (An ) 6= 0} } Was macht der Algorithmus jetzt in der Stufe 1? Zuerst kommt die IF-Abfrage in Zeile 7. Diese Abfrage ist so nicht programmierbar (auf jeden Fall nicht in endlicher Zeit), trotzdem wird jetzt angenommen, das diese Anfrage immer ein Ergebnis liefert und sich der Algorithmus somit immer für einen der beiden Zweige entscheidet. Man kann sich die Abfrage so vorstellen: Das Atom A n (in der ersten Stufe A1 ) wird in alle Modelle Ai eingesetzt und überprüft, ob unendlich oft 1 herauskommt. Zur Erinnerung: Die Ai sind die Modelle für die Formelmengen Mn , die in Schritt 2 definiert wurden. Auch wenn diese Abfrage nicht programmierbar ist, liefert sie immer eine Antwort, entweder es gibt unendliche viele Modelle Ai für die A( A1 ) = 1 oder nicht. Angenommen es gibt unendlich viele, was passiert dann? Das Modell A (das bis jetzt aus der leeren Menge besteht) wird um (A 1 , 1) erweitert. Es weist also fortan dem Atom A1 den Wert 1 zu. Was in Zeile 9 mit der Indexmenge I passiert, wird gleich erklärt. Jetzt erst mal zu dem ELSEZweig. Falls nicht unendlich viele Modelle Ai gibt, die A1 1 zuweisen, dann wird der ELSE-Zweig ausgeführt. Dieser mach genau dasselbe nur erweitert er A um (A1 , 0). Das Modell weist A1 also fortan den Wert 0 zu. Also, was soll die Veränderung der Indexmenge I? Der Grund für die Veränderung der Indexmenge liegt genau in dem Wort fortan im letzten Absatz. Wenn das Modell dem Atom A1 den Wert 1 zuweist, dann soll das im nächsten Schritt auch noch so bleiben. Im nächsten Schritt n = 2 sucht man nach unendlich vielen Modellen Ai für die Ai (A2 ) = 1 ist. Diese Modellen sollen aber natürlich nicht dem widersprechen, was in Schritt 1 bereits fix gemacht wurde. Ein Modell der Form Ai = {(A1 , 0), (A2 , 1), . . .} ist also unbrauchbar, da es A1 nicht den Wert 1 sondern 0 zuordnet. Deswegen geht man brutal vor und verwirft im Schritt 1 gleich alle Modelle, die so aussehen. Das wird erreicht, indem einfach die Indizes der untauglichen Modelle gestrichen werden. Genau das passiert in Zeile 9. Interessant ist, dass dies einfach so geht, aber die unendliche Anzahl an Modellen Ai macht’s möglich. 5. Das Modell A für die Formelmenge M wurde nun konstruiert. Es muss nur noch gezeigt werden, dass es sich auch tatsächlich um ein Modell handelt. Man nimmt sich eine beliebige Formel F aus M. In F können nur endlich viele atomare Formel vorkommen, z. B. l Stück. Das heisst F setzt sich zusammen aus den atomaren Formeln A1 , . . . , Al . Das heisst, F ist Element von Ml und damit auch von Ml+1 ⊆ Ml+2 ⊆ . . .. Daraus folgt, dass Al und Al+1 , Al+2 , . . . Modelle für F sind. Wichtig ist nun, dass die Indexmenge I in jedem Schritt zwar ausgedünnt wird, da I aber zu Beginn unendlich ist, bleibt sie immer unendlich. Das heisst aber auch, das in der Stufe l des Algorithmus immer noch unendliche viele Indizes i ∈ I vorhanden sind. Wenn es noch unendlich viele Indizes gibt, dann gibt es aber sicher auch eins mit i ≥ l. Für diese i gilt: Ai (A1 ) = A(A1 ), . . . , Ai (Al ) = A(Al ) und deshalb ist A ein Modell für F . Da wir das F vollkommenen beliebig aus M gewählt haben, gilt diese Schlussfolgerung für alle F und damit ist A Modell für M. i Obere Schranke für Komplexität der Resolution mit Beweis. 11.2.13 Sei F eine Klauselmenge mit den Atomformeln A1 , . . . , An . Jede der Atomformeln kann in einer Klausel positive vorkommen, negativ vorkommen, positive und negativ vorkommen oder gar nicht vorkommen. Diese vier Möglichkeiten führen zu höchstens 4n verschiedenen Klauseln in Res∗ (F ). In jedem Schritt 154 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.2. Aussagenlogik von Resi (F ) nach Resi+1 (F ) kann nur eine Klausel hinzukommen. Daher ist 4n eine grobe obere Schranke für die Komplexität. 11.2.14 Berechenbarkeit der Aussagenlogik im Vergleich zur Prädikatenlogik? Die Aussagenlogik ist im Gegensatz zur Prädikatenlogik entscheidbar. i Ist die Aussagenlogik entscheidbar? 11.2.15 Die Aussagenlogik ist entscheidbar, da mit Hilfe der Wahrheitstabelle jede Formel entscheidbar ist. 11.2.16 Was können sie über Aussagenkalküle sagen? Hilbert-Kalkül. technisch sehr einfach direkt kaum benutzbar Natürliches Schliessen. technisch Aufwendiger, es gibt lokale Annahmen korrespondiert zum natürlichen/mathematischen Beweisen direkt mit Hilbert-Kalkül verwandt Problem: Beweissuche (→ E hat nicht die Teilformeleigenschaft) aber jede Tautologie hat einen Beweis mit Teilformeleigenschaft Sequenzen-Kalkül. technisch aufwendiger Ideal zur Beweissuche liefert Gegenmodell Für Genaueres siehe (11.2.3), (11.2.23), (11.2.24) und (11.2.25). 11.2.17 Wozu dient ein Kalkül? Ein Kalkül ist eine Kollektion rein mechanisch anzuwendender syntaktischer Umformungsregeln. Die Aufgabenstellung ist der Nachweis der Unerfüllbarkeit einer Formel. ein Kalkül muss korrekt und vollständig sein. 11.2.18 Nennen Sie das Deduktionstheorem der Aussagenlogik. Seien F und G aussagenlogische Formeln und M eine Menge von Formeln, dann gilt: M ∪ {F } |= G 11.2.19 Bilden nicht und =⇒ M |= (F → G) und eine Basis? Ja. A ∨ B kann dargestellt werden durch ¬(¬A ∧ ¬B). 11.2.20 Gibt es eine Basis mit nur einem Operator? DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 155 Kapitel 11. Logik Ja, NAND und NOR. Für NAND gilt: ¬F F ∧G F ∨G = NAND(F, F ) = ¬(F ∧ F ) = ¬F = NAND(NAND(F, G), NAND(F, G)) = NAND(¬(F ∧ G), ¬(F ∧ G)) = ¬((¬(F ∧ G)) ∧ (¬(F ∧ G))) = ¬(¬(F ∧ G)) = F ∧G = NAND(NAND(F, F ), NAND(G, G) NAND(¬F, ¬G) ¬(¬F ∧ ¬G) = = F ∨G = Für NOR gilt: ¬F F ∧G = NOR(F, F ) = ¬(F ∨ F ) = ¬F = NOR(NOR(F, F ), NOR(G, G)) = NOR(¬F, ¬G) = = F ∨F ¬(¬F ∨ ¬G) F ∧G = NOR(NOR(F, G), NOR(F, G)) = NOR(¬(F ∨ G), ¬(F ∨ G)) = = = ¬(¬(F ∨ G) ∨ ¬(F ∨ G)) ¬(¬(F ∨ G)) F ∨G 11.2.21 Erfüllbarkeitstest für Hornformeln? Eine Hornformel F kann mit folgendem Algorithmus auf Erfüllbarkeit getestet werden. 1. Für jede Teilformel der Form (1 → A), markiere A. 2. solange es in F eine Teilformel G der Form (A1 ∧ . . . ∧ An → B) oder (A1 ∧ . . . ∧ An → 0) wobei alle A1 , . . . An bereits markiert sind, B jedoch nicht, tue folgendes: Falls G die erste Form hat, markiere jedes Vorkommen von B, andernfalls unerfüllbar ausgeben und anhalten. 3. Wenn der Algorithmus bis hier läuft, gibt erfüllbar aus und stoppe. Die erfüllende Belegung wird durch die Markierung angezeigt. A(Ai ) = 1 falls Ai markiert ist. Dieser Algorithmus ist korrekt und hat Laufzeit O(n), wobei n die Anzahl der Atome ist. 11.2.22 Was ist der Modus Ponens? Eine Regel des natürlichen Schliessens: φ φ→ψ (→−e) ψ i Erzählen Sie etwas zum natürlichen Schliessen. 11.2.23 156 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.2. Aussagenlogik Im Gegensatz zum Hilbert-Kalkül erlaubt natürliches Schliessen das Schliessen unter lokalen Annahmen. Beim natürlichen Schliessen werden Herleitungsbäume konstruiert. Diese werden mit ihrer Wurzel unten und den Blätter oben geschrieben. Formeln an den Blättern heissen Annahmen, die Formeln an der Wurzel heisst Konklusion. Annahmen können entweder gestrichen oder offen sein. Die Menge der korrekten Herleitungsbäume ist induktiv definiert: 1. F ist ein Beweisbaum mit der offenen Annahme F . 2. Durch die Regeln können induktiv neue Beweisbäume gebildet werden. Die Regeln. Konjunktion. Eine Konjunktion kann eingeführt A B (∧−i) A∧B oder eliminiert werden A∧B A∧B (∧−e) (∧−e). A B Implikation. Wenn B eine Herleitung von A ist, kann man A → B schreiben und die Annahme A streichen. [A] .. . B (→−i) A→B Zur Elimination einer Implikation dient der Modus Ponens. A→B (→−e) B A Disjunktion. Eine Disjunktion kann durch eine der beiden folgenden Regeln eingeführt werden. A (∨−i) A∨B B (∨−i) A∨B Um aus einer Disjunktion A ∨ B etwas schliessen zu können, muss man es sowohl aus A wie aus B schliessen können. [A] [B] .. .. . . A∨B C C (∨−e) C Negation. Die Verneinung einer Annahme ist gerechtfertigt, wenn ihre Annahme zu einem Widerspruch geführt hat. [A] .. . ⊥ (¬−i) ¬A Eine Aussage und ihre Verneinung können nicht gleichzeitig gelten. A ¬A (¬−e) ⊥ Verum. Die wahre Aussage darf immer behauptet werden. A (>−i) > Falsum. Aus der falschen Aussage darf alles gefolgert werden. ⊥ (⊥−e) A DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 157 Kapitel 11. Logik Reductio ad absurdum. Der Widerspruchsbeweis [¬A] .. . ⊥ A (raa) Beispiel. Die Folgerungsbeziehung ¬A |= A → B ist richtig, das Beispiel zeigt einen Herleitungsbaum. ¬A [A] ⊥ B A→B (¬−e) (⊥−e) (→−i) Hier wurde mit der Annahme ¬A begonnen und die temporäre Annahme A hinzugenommen. Daraus kann ⊥ geschlossen werden. Aus ⊥ kann wiederum alles geschlossen werden, in diesem Falle B. Im letzten Schritt wird → eingeführt, dafür darf die temporäre Annahme A gestrichen werden. Korrektheit und Vollständigkeit. Natürliches Schliessen und der Hilbert-Kalkül sind äquivalent. Da der Hilbert-Kalkül korrekt ist, ist auch natürliches Schliessen korrekt. Natürliches Schliessen ist vollständig, da es die Resolution simulieren kann und die Resolution vollständig ist. 11.2.24 Erzählen Sie etwas zu Hilbert-Kalkülen. Hilbert-Kalküle sind sehr einfach, eignen sich aber schlecht zur Beweissuche. Ein Hilbert-Kalkül besteht aus einer Menge von Formeln (Axiomen) und einer Menge von Schlussregeln der Form F1 . . . F n Prämissen = . F Konklusion Wenn n = 0 dann ist die Regel ein Axiom. Eine Herleitung einer Formel F aus einer Formelmenge M ist eine Sequenz F1 , . . . , Fk mit Fk = F und für i ≤ k gilt Fi ist ein Axiom oder n es gibt eine Regel G1 ...G so dass {G1 , . . . Gn } ⊆ {F1 , . . . Fi−1 }, d. h. Fi wurde auch aus F G hergeleitet. Man schreibt M `H F . Die Herleitung ist ein Beweis von F . Es gibt zwei Grundaxiome. A1 F → (G → F ) für beliebige Formeln F, G A2 (F → G → H) → (F → G) → F → H Kettenschluss Eine Schlussregel, den Modus Ponens. F →G G F Beispiel. Die Herleitung von `H A → A. 1. 2. 3. 4. 5. (A → (A → A) → A) → (A → (A → A)) → A → A A → ((A → A) → A) (A → A → A) → A → A A→A→A A→A (A2) (A1) (MP 1,2) (A1) (MP 3,4) Deduktionstheorem. In jedem Hilbert-Kalkül, das mindestens die Axiome A1 und A2 enthält und nur den Modus Ponens als Regel besitzt gilt M ∪ {F } `H G genau dann, wenn M `H F → G 158 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.2. Aussagenlogik Vollständiges Axiomensystem. A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 F → (G → F ) (F → G → H) → (F → G) → F → H F ∧G→F F ∧G→G F →G→F ∧G F →F ∨G G→F ∨G F ∨ G → (F → H) → (G → H) → H (¬F → ⊥) → F ¬F → F → ⊥ (F → ⊥) → ¬F Korrektheit. Es gilt `H F ⇒ |= F Der Beweis geht mit Induktion über die Länge der Herleitung `H F 1. Alle Axiome sind Tautologien. 2. Der Modus Ponens überführt Tautologien in Tautologien. Vollständigkeit. Es gilt |= F ⇒ `H F Beweis. 1. Hilbert-Kalkül ist äquivalent zum Natürlichen Schliessen. 2. Natürliches Schliessen kann Resolution simulieren. 3. Resolution ist vollständig, siehe 11.2.5. 11.2.25 Erzählen Sie etwas zum Sequenzen-Kalkül. Das Ziel des Sequenzen-Kalküls ist die Beweissuche. Es werden immer Sequenzen der Form Γ ⇒ ∆ bewiesen wobei Γ, ∆ Multimengen von Formel sind. Intuitiv gilt, F1 , . . . Fn ⇒ G1 , . . . , Gn ist herleitbar falls |= F1 ∧ . . . ∧ Fn → G1 ∨ . . . ∨ Gn gilt. Dieser Zusammenhang wird wie folgt notiert: |Γ ⇒ ∆| = ^ Γ→ _ ∆ Es gibt für jeden Junktor eine rechte und eine linke Regel: Konjunktion. Γ, F, G ⇒ ∆ (∧−L) Γ, F ∧ G ⇒ ∆ Γ ⇒ ∆, F Γ ⇒ ∆, G (∧−R). Γ ⇒ ∆, F ∧ G Disjunktion. Γ, F ⇒ ∆ Γ, G ⇒ ∆ (∨−L) Γ, F ∨ G ⇒ ∆ Γ ⇒ ∆, F, G (∨−R). Γ ⇒ ∆, F ∨ G Implikation. Γ ⇒ F, ∆ Γ, G ⇒ ∆ (→−L) Γ, F → G ⇒ ∆ Γ, F ⇒ ∆, G (→−R). Γ ⇒ ∆, F → G Negation. Γ ⇒ F, ∆ (¬−L) Γ, ¬F ⇒ ∆ DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs Γ, F ⇒ ∆ (¬−R). Γ ⇒ ∆, ¬F 159 Kapitel 11. Logik Da bei jeder Regel die Formeln der Konklusion auch in der Prämisse stehen, ist die Rückwärtsanwendung einer deterministisch, Dies nennt man Teilformeleigenschaft. Für jede Regel S1 Sn S und jede passende Belegung A gilt A |= |S| genau dann, wenn A |= |S1 | und . . . und A |= |Sn | Die Korrektheit des Sequenzen-Kalküls, also dass `G S folgt, dass |= |S| gilt, wird mit einem Induktionsbeweis über die Grösse der Herleitung von S gezeigt. Das Sequenzen-Kalküls hat eine weitere interessante Eigenschaft: Jeder fehlgeschlagene Beweisversuch für S liefert ein Gegenmodell für |S|, das heisst ein Modell für ¬|S|. Dadurch ist auch gleich die Vollständigkeit beweisen, da jeder Beweisversuch immer zu einem Modell für |S| oder ¬|S| führt. Beispiel. Aufgabe ist es zu zeigen, dass ⇒ A ∨ ¬A gilt. Axiom A⇒A (¬−R) ⇒ A, ¬A (∨−R) ⇒ A ∨ ¬A 11.2.26 Was ist eine Wahrheitstafel? Eine Wahrheitstafel zeigt alles passenden Belegungen einer Formel. 11.2.27 Ist die Formel ((A → B) → C) ↔ (A → (B → C)) eine Tautologie? Nein, das würde bedeuten, dass das Assoziativgesetz für die Implikation gilt und das tut es nicht. 11.3 11.3.1 Prädikatenlogik Wie gehen Sie vor, wenn Sie die Unerfüllbarkeit einer prädikatenlogischen Formel F zeigen möchten Um die prädikatenlogische Resolution auf eine Formel F anzuwenden, müssen folgende Schritte durchlaufen werden. 1. Skolemnormalform. Um mit der Formel F überhaupt etwas Anfangen zu können muss zuerst die Skolemform der Formel erstellt werden. Den Weg dorthin kann man wieder in mehrere Schritte zerlegen und wird an der Formel F = ¬∃x P (x, z) ∨ ∀yQ x, f (y) ∨ ∀yP g(z, y), z vorgeführt. a) Bereinigung. F wird durch gebundenes Umbenennen der Variablen in eine äquivalente , bereinigte Form F1 überführt. Eine Formel ist in bereinigter Form wenn es keine Variablen mehr gibt die sowohl frei und gebunden vorkommen und hinter allen Quantoren verschiedene Variablen stehen. Das y im zweiten Disjunktionsglied wird in w umbenannt und es ergibt sich: F1 = ¬∃x P (x, z) ∨ ∀yQ x, f (y) ∨ ∀wP g(z, w), z Diese Formel ist äquivalent zu F . b) Freie Variablen beseitigen. Falls in der Formel F1 noch freie Variable y1 , . . . , yn vorkommen, ersetzt man F1 durch F2 = ∃y1 . . . ∃yn F1 . F2 ist erfüllbarkeitsäquivalent zu F1 und F2 . Im Beispiel ist z eine ungebundene Variable deshalb führt man ∃z ein: F2 = ∃z ¬∃x P (x, z) ∨ ∀yQ x, f (y) ∨ ∀wP g(z, w), z 160 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.3. Prädikatenlogik c) Pränexform. Jetzt wird eine zu F2 äquivalent und damit zu F erfüllbarkeitsäquivalente Formel F3 in Pränexform hergestellt. Eine Formel ist in Pränexform, wenn alle ihre Quantoren am Anfang der Formel stehen. Das ergibt F3 = ∃z∀x∃y∀w ¬P (x, z) ∧ ¬Q x, f (y) ∨ P g(z, w), z d) Skolemform. Jetzt werden die Existenzquantoren durch den Übergang zur Skolemform eliminiert. Die neue Formel F4 ist weiterhin erfüllbarkeitsäquivalent zu F . In diesem Falle wird der z durch a und y durch h(x) substituiert. F4 = ∀x∀w ¬P (x, a) ∧ ¬Q x, f (h(x)) ∨ P g(a, w), a e) KNF. Die Matrix von F4 wird in KNF umgeformt: F5 = ∀x∀y((P (g(a, w), a) ∨ ¬P (x, a)) ∧ (P (g(a, w), a) ∨ ¬Q(x, f (h(x))))) Als Klauselmenge dargestellt: F5∗ = {{P (g(a, w), a), ¬P (x, a)}, {P (g(a, w), a), ¬Q(x, f (h(x)))}} Bis jetzt ist noch nicht wirklich etwas passiert. Diese Schritte galten nur der Erstellung der Skolemform, die im weiteren Verlauf benötigt wird. Wichtig ist, dass die entstandene Formel F5 trotz aller Umformungen erfüllbarkeitsäquivalent zu F ist. → Motivation der nächsten Schritte. Die nächsten Schritte hängen jetzt erst mal ein bisschen in der Luft. Wichtig ist, nicht aus den Augen zu verlieren, das es im Endeffekt um das Zeigen der Unerfüllbarkeit geht. Um die Unerfüllbarkeit zu zeigen benötigt man eine Menge E(F ) namens Herbrand-Expansion. Die nächsten Schritte beschäftigen sich mit dem Finden dieser Menge. Das kann man sich am besten vorstellen, wenn man vom Satz von Herbrand (11.3.17) zurück denkt. Dieser Satz besagt, dass eine Aussage in Skolemform F genau dann unerfüllbar ist, wenn es eine endliche Teilmenge von E(F ) (der Herbrand-Expansion) gibt, die im aussagenlogischen Sinne unerfüllbar ist. Der Beweis dieses Satzes hängt aber davon ab, ob eine erfüllbare Formel ein Modell mit abzählbarer Grundmenge besitzt. Das dem so ist, besagt der Satz von Löwenheim-Skolem (11.3.21). Er besagt, dass jede erfüllbare prädikatenlogische Formel ein Modell mit abzählbarer Grundmenge besitzt und zwar ein Herbrand-Modell, das eine Herbrand-Struktur ist. 2. Herbrand-Struktur. Zum Beispiel ist die Herbrand-Struktur (11.3.10) für die Formel F F = ∀x∀y∀zP (x, f (y), g(z, x)) wie folgt definiert. UA = D(F ) = {a, f (a), g(a, a), f (g(a, a)), f (f (a)), g(a, f (a), g(f (a), a, . . .} und es gilt: f A (a) A f (f (a)) A f (g(a, a)) = f (a) = f (f (a)) = f (g(a, a)) .. . 3. Herbrand-Expansion. Jetzt wird die Herbrand-Expansion bestimmt. Die Herbrand-Expansion entsteht in dem die Variablen in F ∗ in jeder möglichen weise durch die Terme in D(F ) substituiert werden. Für die Formel F = ∀x∀y∀zP (x, f (y), g(z, x)) DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 161 Kapitel 11. Logik ergeben sich P (a, f (a), g(a, a)) P (f (a), f (a), g(a, f (a))) P (a, f (f (a)), g(a, a)) durch [x/a][y/a][z/a] durch [x/f (a)][y/a][x/a] durch .. . [x/a][y/f (a)][x/a] Nach dem Satz von Herbrand (11.3.17) ist eine Aussage F in Skolemform genau dann unerfüllbar, wenn eine endliche Teilmenge von E(F ) im aussagenlogischen Sinne unerfüllbar ist. D. h. man muss jetzt nur per Aussagenlogik zeigen, dass E(F ) unerfüllbar ist und ist fertig. Da kommt nun die Resolution ins Spiel. Im Folgenden seien F1 , F2 . . . eine Aufzählung aller Formeln in E(F ). 4a. Grundresolution. Die Aufgabe ist nun zu zeigen, das E(F ) im aussagenlogischen Sinne unerfüllbar ist. Dies kann sehr einfach mit dem Grundresolutionsalgorithmus durchgeführt werden (11.3.11). Die Grundresolution arbeitet auf den Element von E(F ) und damit auf Grundinstanzen der Klausel von F ∗ . Sie heissen Grundinstanzen, da alle Variablen durch variablenfreie Terme ersetzt wurden. Beispiel. Gegeben ist die unerfüllbare Formel, ihre Matrix in Klauselform, D(F ) und E(F ) F F∗ = ∀x(P (x) ∧ ¬P (f (x))) = (P (x) ∧ ¬P (f (x))) = {{P (x)}, {¬P (f (x))}} D(F ) = {a, f (a), f (f (a)), . . .} E(F ) = {(P (a) ∧ ¬P (f (a))), (P (f (a)) ∧ ¬P (f (a)))), . . .} Schon die ersten beiden Substitutionen [x/a] und [x/f (a)] liefern eine unerfüllbare Klauselmenge. {P (a)} {¬P (f (a))} {P (f (A))} {¬P (f (f (a)))} 2 Die Grundresolution ist sehr ineffizient, da alle Grundsubstitutionen durch probiert werden müssen. Eine Alternative bietet die prädikatenlogische Resolution. 4b. Prädikatenlogische Resolution. Wie gesagt ist die ausschliessliche Resolution von Grundinstanzen sehr ineffizient, besser wäre es nur die Substitutionen durchzuführen, die auf dem Weg zum Ziel liegen. Die Grundidee dabei ist, prädikatenlogische Resolventen aus prädikatenlogischen Klausel zu erzeugen. Dabei ist mit jedem Resolutionsschritt ein Substitutionsschritt verbunden. Ziel ist es wie bei der aussagenlogischen Resolution, bestimmte Literale in zwei Ausgangsklauseln zueinander komplementär zu machen. Ziel hierbei ist es Substitutionen zu finden, die zwei Literale unifizieren. Beispiel. Gegeben sind zwei prädikatenlogische Klauseln K1 = K2 = {P (f (x)), ¬Q(z), P (z)} {¬P (x), R(g(x), a)} R ist ein prädikatenlogischer Resolvent von K1 , K2 wenn die drei Bedingungen von (11.3.16) gelten. Ein prädikatenlogischer Resolvent von K1 , K2 ist R = {¬Q(f (x)), R(g(f x)), a)} R erfüllt die drei Bedingungen: 1. Es gibt zwei Substitutionen s1 und s2 , so dass K1 und K2 keine gemeinsamen Variablen enthalten. Man wählt z. B. s1 = [ ] und s2 = [x/u]. Das ergibt 162 K1 s 1 = K2 s 2 = K1 [ ] = {P (f (x)), ¬Q(z), P (z)} K2 [x/u] = {¬P (u), R(g(u), a)} http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.3. Prädikatenlogik 2. Jetzt muss sich eine Literalmenge aus K1 s1 , K2 s2 finden lassen, die unfizierbar ist. Eine Wahl ist L {P (f (x)), P (z), ¬P (u) } | {z } | {z } aus K1 s1 aus K2 s2 = {¬P (f (x)), ¬P (z), ¬P (u)} = Mit dem Unifikationsalgorithmus kann nun der allgemeinste Unifikator sub bestimmt werden. Dieser liefert in zwei Schritten sub = [z/f (x)][u/f (x)] 3. Wenn jetzt noch die dritte Bedingung gilt, handelt es sich bei R um einen Resolventen. Ein Resolvent ist nur dann ein Resolvent, wenn er folgende Form hat. R = ((K1 s1 − {L1 , . . . , Lm }) ∪ (K2 s2 − {L01 , . . . , L0n }))sub. Einsetzen der obigen Klausel und Substitutionen ergibt R = = = = = (({P (f (x)), ¬Q(z), P (z)} − {P (f (x)), P (z)}) ∪ ({¬P (u), R(g(u), a)} − {¬P (u)}))[z/f (x)][u/f (x)] ({¬Q(z)} ∪ {R(g(u), a)})[z/f (x)][u/f (x)] ({¬Q(z), R(g(u), a)})[z/f (x)][u/f (x)] ({¬Q(f (x)), R(g(u), a)})[u/f (x)] {¬Q(f (x)), R(g(f (x)), a)} Die Abbildung zeigt den Resolutions-/Substitutionsschritt noch einmal graphisch. {P (f (x)), ¬Q(z), P (x)} {¬P (x), R(g(x), a)} s1 s2 sub = [] = [x/u] = [z/f (x)][u/f (x)] {¬Q(f (x)), R(g(f (x)), a)} Auf diese Weise können nun nur die Substitutionsschritte durchgeführt werden, die zum Finden der leeren Klausel benötigt werden. 11.3.2 Um was erweitert die Prädikatenlogik die Aussagenlogik? Die Prädikatenlogik erweitert die Aussagenlogik um Quantoren, Funktions- und Prädikatensymbole. 11.3.3 Normalformen der Prädikatenlogik? siehe 11.3.22, 11.3.23 und 11.3.26 11.3.4 i Ist die Herbrand-Expansion abzählbar? Die Herbrand-Expansion entsteht in dem die Variablen in F ∗ in jeder möglichen weise durch die Terme in D(F ) substituiert werden. Das D(F ) abzählbar ist, ist auch E(F ) abzählbar. 11.3.5 Ist die Prädikatenlogik entscheidbar? Das Erfüllbarkeitsproblem der Prädikatenlogik ist nicht entscheidbar. Beweis durch Reduktion auf das Postsche Korrespondenzproblem. 11.3.6 Nennen Sie mir ein Kalkül für die Prädikatenlogik? Resolution. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 163 Kapitel 11. Logik 11.3.7 Was ist eine Struktur? Eine Struktur is ein Paar A = (UA , IA ) wobei UA eine beliebige aber nicht leere Menge ist. IA ist eine Abbildung, die jedem k-stelligen Prädikatensymbol P ein k-stelliges Prädikat über UA zuordnet, jedem k-stelligen Funktionssymbol f eine k-stellige Funktion auf UA zuordnet, jeder Variablen x eine Element der Grundmenge UA zuordnet. 11.3.8 Prädikatenlogische Resolution mit Unifikation am Beispiel ∃x(P (x) → ∀xP (x)). Dieses Beispiel ist so einfach, dass man wirklich Schritt für Schritt vor gehen kann, da das HerbrandUniversum endlich ist, kann man ganz in Ruhe die Herbrand-Expansion bestimmen und dann erst resolvieren. 1. Skolemform. Um mit der Formel F überhaupt etwas Anfangen zu können muss zuerst die Skolemform der Formel erstellt werden. Zur Übersicht wird erstmal die Implikation ersetzt. F = ∃x(P (x) → ∀xP (x)) = ∃x(¬P (x) ∨ ∀xP (x)) a) Bereinigung. F wird durch gebundenes Umbenennen der Variablen in eine äquivalente , bereinigte Form F1 überführt. Hier wurde die Variable beim Allquantor in y umbenannt. Es ergibt sich: F1 = ∃x(¬P (x) ∨ ∀yP (y)) Diese Formel ist äquivalent zu F . b) Freie Variablen beseitigen. Die Formel enthält keine freien Variablen, der Schritt entfällt daher und es gilt: F2 = F1 = ∃x(¬P (x) ∨ ∀yP (y)) c) Pränexform. Jetzt wird eine zu F2 äquivalente und damit zu F erfüllbarkeitsäquivalente Formel F3 in Pränexform hergestellt. Eine Formel ist in Pränexform, wenn alle ihre Quantoren am Anfang der Formel stehen. Das ergibt F3 = ∃x∀y(¬P (x) ∨ P (y)) d) Skolemform. Jetzt werden die Existenzquantoren durch den Übergang zur Skolemform eliminiert. Die neue Formel F4 ist weiterhin erfüllbarkeitsäquivalent zu F . In diesem Falle wird der einzige Existenzquantor gestrichen und durch das nullstellige Funktionssymbol a ersetzt. F4 = ∀y(¬P (a) ∨ P (y)) e) KNF. Die Matrix von F4 wird in KNF umgeformt und als Klauselmenge dargestellt. Da die Matrix von F4 bereits in KNF ist, ergibt sich F5 = F4 = ∀y(¬P (a) ∨ P (y)) Als Klauselmenge dargestellt: F5∗ = {{¬P (a), P (y)}} 2. Herbrand-Struktur. In diesem Fall ist es sehr einfach das Herbrand-Universum zu bestimmen, da F keine Funktionssymbol mit einer Stelligkeit grösser 0 enthält. Es gibt eine Konstante a. Sie ist das einzige Element des Herbrand-Universums und es gilt: D(F5 ) = {a} 3. Herbrand-Expansion. Jetzt wird die Herbrand-Expansion E(F5 ) bestimmt. Dies ist beim Beispiel sehr einfach da D(F5 ) nur das einzige Element a hat und F5 nur eine Variable nämlich y hat. Also ersetzt man y durch a und erhält E(F5 ) = {F5∗ [y/a]} = {{¬P (a), P (a)}} 164 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.3. Prädikatenlogik 4. Resolution. Hier gibt es leider nichts zu resolvieren. Unerfüllbarkeit kann nicht gezeigt werden. Diese geht aber nur wegen dem endlichen Herbrand-Universum. 11.3.9 Was ist das Herbrand-Universum? Das Herbrand-Universum D(F ) einer Formel F in Skolemform ist die Menge aller variablenfreien Terme, die aus allen Bestandteilen von F gebildet werden können. Es ist induktiv definiert: 1. Alle in F vorkommenden Konstanten sind in D(F ). Falls F keine Konstante enthält, so ist a in D(F ). 2. Für jedes in F vorkommende n-stellige Funktionssymbol f und Terme t1 , . . . tn in D(F ) ist der Term f (t1 , . . . tn ) in D(F ). Beispiel. Gegeben seien die Formeln F G = ∀x∀y∀zP (x, f (y), g(z, x)) = ∀x∀yQ(c, f (x), h(y, b)) Bei F liegt der Spezial fall vor, da F keine Konstante enthält wird a in das Herbrand-Universum aufgenommen: D(F ) = {a, f (a), g(a, a), f (g(a, a)), f (f (a)), g(a, f (a), g(f (a), a, . . .} Bei G sind Konstanten c und b vorhanden, daher: D(G) = {c, b, f (c), f (b), h(c, c), h(c, b), h(b, c), h(b, b), f (f (c)), f (f (b)), . . .} Man sieht, das Herbrand-Universum ist unendlich, sobald die Formel ein Funktionssymbol mit einer Stelligkeit > 0 enthält. Man sieht auch, dass es rekursiv aufzählbar ist. 11.3.10 Was ist eine Herbrand-Struktur? Wenn F eine Aussage in Skolemform ist, dann heisst jede zu F passende Struktur A = (U A , IA ) eine Herbrand-Struktur, falls folgendes gilt: 1. UA = D(F ) (das Herbrand-Universum) 2. für jedes in F vorkommende n-stellige Funktionssymbol f und t1 , . . . tn ∈ D(F ) ist f A (t1 , . . . tn ) = f (t1 , . . . tn ). Beispiel. Für die Formel F = ∀x∀y∀zP (x, f (y), g(z, x)) ist UA = D(F ) = {a, f (a), g(a, a), f (g(a, a)), f (f (a)), g(a, f (a), g(f (a), a, . . .} und es gilt: f A (a) A f (f (a)) f A (g(a, a)) = f (a) = f (f (a)) = f (g(a, a)) .. . Die Wahl von P A bleibt an dieser Stelle noch offen. Wichtig ist, dass durch Punkt 2 der Definition Syntax und Semantik gleichgeschaltet werden. Die Interpretation eines Terms liefert den Term, es gilt also A(t) = t. DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 165 Kapitel 11. Logik 11.3.11 Geben Sie den Grundresolutionsalgorithmus an. Der Grundresolutionsalgorithmus ist ein Resolutionsalgorithmus, der wie der Gilmore-Algorithmus funktioniert. Wenn F eine prädikatenlogische Formel ist, bezeichnen F1 , F2 . . . alle Formel ihrer HerbrandExpansion E(F ). Der Algorithmus erhält als Eingabe die Mengen Fi . Grundresolution(Fi ) 1 i←0 2 M ←∅ 3 do { 4 i←i+1 5 M ← M ∪ Fi 6 M ← Res∗ (M ) 7 } until (2 ∈ M ) 8 return unerfüllbar Wenn die Formel F unerfüllbar ist, hält der Algorithmus nach endlich vielen Schritten. 11.3.12 Was ist eine Grundsubstitution? Bei einer Grundsubstitution werden alle freien Variablen durch variablenfreie Terme ersetzt. Die Substitutionen zur Definition der Herbrand-Expansion sind Grundsubstitutionen. 11.3.13 Was ist ein Unifikator und was ist ein allgemeinster Unifikator? Eine Substitution sub ist ein Unifikator einer endlichen Menge von Literalen L = {L 1 , . . . Lk }, falls L1 sub = L2 sub = . . . = Lk sub. D. h. durch Anwendung der Substitution sub auf jedes Literal in L entsteht ein und dasselbe Literal. Man schreibt für diesen Sachverhalt auch |Lsub| = 1 sagt L ist unifizierbar. Ein Unifikator sub einer Literalmenge L heisst allgemeinster Unifikator von L, falls für jeden Unifikator sub0 von L gilt, dass es eine Substitution s gibt mit sub0 = subs. Wobei subs die Hintereinanderausführung der Substitution subs und s beschreibt. 11.3.14 Wie funktioniert der Unifikationsalgorithmus? Der Unifikationsalgorithmus überprüft ob eine Literalmenge L unifizierbar ist und gibt in diesem Fall den allgemeinsten Unifikator aus. Als Eingabe erhält er eine nicht-leere Literalmenge L. In jedem Schritt versucht er einen Unifikator zu finden, falls dies nicht möglich ist, bricht er ab, ansonsten wird der neue Unifikator an den im vorherigen Schritt bestimmten, angehängt. Unifikation(L) 01 sub ← [ ] //die leere Substitution 02 while (|Lsub| > 1) { 03 durchsuche die Literale in Lsub paarweise von links nach rechts bis erste 04 Position gefunden, wo sich zwei Literale L1 und L2 unterscheiden. 05 if (keines der beiden Zeichen ist Variable) 06 return nicht unifizierbar 07 else { 09 sei x die Variable und t der im anderen Literal beginnenden Term 10 if (x kommt in t vor) 11 return nicht unifizierbar 12 else 13 sub ← sub[x/t] //Hintereinanderausführung von sub und [x/t] 14 } 15 } 16 return allgemeinster Unifikator: ∪ sub 166 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.3. Prädikatenlogik 11.3.15 Was ist der Gödelsche Unvollständigkeitssatz? Jedes Beweissystem für die Menge der wahren arithmetischen Formeln ist notwendigerweise unvollständig, d. h. es bleiben immer wahre arithmetische Formeln übrig, die nicht beweisbar sind. 11.3.16 Wie ist die Resolution der Prädikatenlogik definiert? Seien K1, K2 und R prädikatenlogische Klauseln. Dann ist R ein prädikatenlogischer Resolvent von K1 und K2 , falls folgendes gilt: 1. Es gibt Substitutionen s1 und s2 , die Variablenumbennungen sind, so dass K1 s1 und K2 s2 keine gemeinsamen Variablen enthalten. 2. Es gibt eine Menge von Literalen L1 , . . . , Lm ∈ K1 s1 (m ≥ 1) und L01 , . . . , L0n ∈ K2 s2 (n ≥ 1), so dass L = {L1 , . . . , Lm , L01 , . . . , L0n } unifizierbar ist. sub ist der allgemeinste Unifikator von L. 3. R hat die Form R = ((K1 s1 − {L1 , . . . , Lm }) ∪ (K2 s2 − {L01 , . . . , L0n }))sub. 11.3.17 Wie lautet der Satz von Herbrand? Eine Aussage F in Skolemform ist unerfüllbar genau dann, wenn es eine endliche Teilmenge von E(F ) gibt, die im aussagenlogischen Sinne unerfüllbar ist. 11.3.18 Was ist das Lifting-Lemma? Seien K1 , K2 zwei prädikatenlogische Klauseln und K10 , K20 seien beliebige Grundinstanzen hiervon, die im aussagenlogischen Sinne resolvierbar sind, so dass R 0 ein Resolvent von K10 und K20 ist. Dann gibt es einen prädikatenlogischen Resolventen R von K1 und K2 , so dass R0 eine Grundinstanz von R ist. Das Lifting-Lemma wird zum Beweis der Vollständigkeit der prädikatenlogischen Resolution benötigt. 11.3.19 Wie lautet der Resolutionssatz der Prädikatenlogik? Sei F eine Aussage in Skolemform mit der Matrix F ∗ in KNF. Dann gilt: F ist unerfüllbar genau dann, wenn 2 ∈ Res∗ (F ∗ ). 11.3.20 Was sind Terme? Term sind induktive definiert: 1. Jede Variable ist ein Term. 2. Falls f eine k-stellige Funktion und t1 , . . . tk Terme sind so ist auch f (t1 , . . . , tk ) ein Term. i Satz von Löwenheim/Skolem mit Beweis? 11.3.21 Jede erfüllbare prädikatenlogische Formel besitzt ein Modell mit abzählbarer Grundmenge. Beweis. Jede prädikatenlogische Formel F kann in eine erfüllbarkeitsäquivalente Aussage G in Skolemform umgewandelt werden. Diese Umformungen sind so, dass jedes Modell für G auch Modell für F ist. Falls F erfüllbar ist, dann ist auch G erfüllbar und besitzt ein Herbrand-Modell, dass auch Modell für F ist. Das Herbrand-Modell besitzt die abzählbare Grundmenge D(F ). 11.3.22 Welche Eigenschaften hat eine bereinigte Formel? Eine Formel heisst bereinigt, wenn es keine Variable gibt, die sowohl gebunden als auch ungebunden vorkommt. Ausserdem müssen hinter allen Quantoren unterschiedliche Variablen stehen. Diese Form erreicht man durch gebundenes Umbenennen (11.3.24). DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 167 Kapitel 11. Logik 11.3.23 Was ist die Pränexform? Eine Formel F ist in Pränexform falls sie folgende Bauart hat Q1 y1 Q2 y2 . . . Q n yn F wobei Qi ∈ {∃, ∀}. In F darf kein Quantor mehr vorkommen. 11.3.24 Was versteht man unter gebundenen Umbenennen? Sei F = QxG mit Qi ∈ {∃, ∀}. Wenn y eine Variable ist, die in G nicht vorkommt, dann gilt F ≡ QyG[x/y] 11.3.25 Was ist eine Matrix? Die Matrix einer Formel F in Skolemform ist diejenige Formel, die man erhält, wenn man alle Quantoren in F samt der dahinter stehenden Variablen streicht. Sie wird mit F ∗ bezeichnet. 11.3.26 Was ist die Skolemform? Eine Formel ist in Skolemform, wenn sie bereinigt ist, in Pränexform ist und nur Allquantoren enthält. 11.3.27 Was ist eine Theorie? Eine Theorie ist eine Menge von Formel T, die gegenüber Folgerbarkeit abgeschlossen ist. D.h. T ist eine Theorie wenn für alle F1 , F2 , . . . Fn ∈ T und alle Formeln G gilt: wenn G eine Folgerung von {F1 , F2 , . . . , Fn } ist, dann ist auch G ∈ T. Die Formel, die Element einer Theorie sind, heissen auch Sätze. 11.3.28 Was ist ein Axiom? Theorien können auf zwei Arten definiert werden: 1. Bei der modelltheoretischen Methode gibt man eine Struktur A vor und nimmt als Theorie alle Formel die unter A gelten: T h(A) = {F | A |= F } 2. Bei der axiomatischen Methode gibt man ein Axiomensystem, also eine Menge von Formeln M vor. Diese Theorie besteht dann aus allen Formel die aus den Formel der Menge M folgerbar sind. Die Element der Menge M heissen Axiome. Es gilt: Cons(M) = {G | G folgt aus {F1 , . . . Fn } ∈ M} 11.4 11.4.1 Beweisideen Unentscheidbarkeit der Prädikatenlogik. Der Beweis erfolgt per Reduktion. Wir suchen einen Algorithmus der jedes beliebige Postsche Korrespondenzproblem (PCP) K in eine prädikatenlogische Formel F = FK überführt, so das K eine Lösung besitzt, genau dann wenn F gültig ist. Das heisst man führt das Gültigkeitsproblem der Prädikatenlogik auf PCP zurück. Falls das Gültigkeitsproblem entscheidbar wäre, müsste auch das PCP entscheidbar sein. Die widerspricht den Kenntnissen der Berechenbarkeitstheorie. 11.4.2 Erfüllbarkeitsäquivalenz der Skolemform. Beweis zeigt, dass jeder Durchlauf der Konstruktions-While-Schleife eine erfüllbarkeitsäquivalente Formel erzeugt. Dazu wird das Überführungslemma verwendet. 168 http://www.deissenboeck.de/faqs | DHP-FAQ v1.0 11.4. Beweisideen 11.4.3 Äquivalenz der Pränexform. Bei der Umwandlung einer Formel F in eine Formel G in Pränexform gilt F ≡ G. Beweis. Der Beweis ist ein Induktionsbeweis über den Formelaufbau. Hier ist alles sehr vereinfacht dargestellt. Induktionsanfang. Wenn F eine atomare Formel ist, dann ist F = G bereits in Pränexform und es gilt F ≡ G. Induktionsschritt. Es gibt 3 Fälle. 1. F hat die Form ¬F1 und G1 = Q1 y1 . . . Qn yn G0 ist die laut Induktionsvoraussetzung existierende zu F1 äquivalent Formel. Dann gilt F ≡ Q1 y1 . . . Qn yn ¬G0 Diese Formel ist in Pränexform. 2. F hat die Form (F1 ◦ F2 ). Es gibt zu F1 und F2 äquivalente Formeln G1 und G2 in Pränexform. Die Formel Q1 y1 . . . Qk yk Q01 z1 . . . Q0l zl (G01 ◦ G02 ) ist äquivalent zu F und in Pränexform. 3. F hat die Form QxF1 . Die gesucht Formel hat die Form QxQ1 y1 . . . Qn yn F10 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 169 Literaturverzeichnis [Cormen et al., 2000] Cormen, Thomas H. et al. Introduction to Algorithms. MIT-Press, 2000 [DS-Faq, 2001] DS-Faq. http://www.deissenboeck.de/faqs [Duden Mathematik, 2000] Duden Rechnen und Mathematik. Dudenverlag, 2000 [Duden Informatik, 1993] Duden Informatik. 2. Aufl. Dudenverlag, 1993 [Graham et. al., 1989] Graham, R.L., D.E. Knuth; Patashnik O. Concrete Mathematics. Addison Wesley, 1989 [Kindler & Manthey, 2001] Kinderl Ekkart; Manthey Steffen Skript zur Vorlesung Automaten, Formale Sprachen und Berechenbarkeit WS 01/02. [Ottmann & Widmayer, 2002] Ottman, Thomas; Widmayer, Peter. Algorithmen und Datenstrukturen. 4. Aufl. Spektrum, Akad. Verl., 2002 [Rechenberg & Pomberger, 1999] Rechenberg, Peter; Pomberger, Gustav (hrsg.) Informatik-Handbuch. 2. Aufl. Hanser, 1999 [Schöning, 1997] Schöning, Uwe. Theoretische Informatik – kurzgefaßt. 3. Aufl. Spektrum, Akad. Verl., 1997 [Schöning, 2000] Schöning, Uwe. Logik für Informatiker. 5. Aufl. Spektrum, Akad. Verl., 2000 DHP-FAQ v1.0 | http://www.deissenboeck.de/faqs 171