Informatik Vorlesung Wintersemester 2003 Johannes Waldmann, HTWK Leipzig 30. Januar 2004 Informatik, Algorithmen (10. 10. 03) Informatik—Überblick • Informatik: Wissenschaft von der Verarbeitung symbolischer Informationen • durch Algorithmen • implementiert als Programm • ausgeführt auf (einem oder mehreren) Rechnern Teilgebiete der Informatik (HKF S. 23 ff). • Theoretische: Automatentheorie, Algorithmentheorie, Berechenbarkeit, Komplexität • Praktische: Programm- und Software-Entwicklung, Dienst- und Arbeitsprogramme (Compiler, Betriebssysteme, Datenbanken, . . . ) • Technische: Hardware (Prozessor, Speicher, Peripherie, Netze, Kommunikation) • Angewandte: Inhalt (1. Semester) • Grundlagen der Informatik: Algorithmen – Definition, Beispiele – Entscheidbarkeit, Komplexität • Praktische Informatik: Software – – – – – – einfache Daten und Kontrollstrukturen Unterprogramme, Klassen, Methoden konkrete Datentypen: Listen, Bäume Algorithmen für Bäume und Graphen abstrakte Datentypen, Spezifikationen, Interfaces effiziente Datenstrukturen: Listen, Bäume, Hashing Inhalt (2. Semester) • Technische Informatik: Hardware – maschinelle Darstellung von Information – Rechner-Aufbau: Prozessor, Speicher – Peripherie-Geräte: Monitore, Drucker, . . . • Praktische/Angewandte Informatik: – Betriebssysteme: Resourcen-Verwaltung und -Teilung – Netze, Kommunikation, Standards, Protokolle Empfohlene Literatur/Links • Webseite zur Vorlesung/Übung, mit Skript, Folien, Aufgaben: http://www.imn.htwk-leipzig.de/ ˜waldmann/ws03/informatik/ • Lehrbuch (Allgemeines, Grundlagen): Horn, Kerner, Forbrig: Lehr- und Übungsbuch Informatik, Fachbuchverlag (dritte Auflage, 1. Band: Grundlagen und Überblick) http://www.inf.tu-dresden.de/˜fachbuch/ • Software: BlueJ http://www.bluej.org/ • Lehrbuch (Programmieren): David J. Barnes / Michael Kölling: Objektorientierte Programmierung mit Java (Eine praxisnahe Einführung mit BlueJ) http://www.pearson-studium.de/main/main. asp?page=shop/bookdetails&ProductID=37199 Organisation, Leistungsnachweise • Vorlesung – ungerade, freitags, 7:30–11:00, Li207 • Seminare: Einschreibung über ein Web-Interface, http://www.imn.htwk-leipzig.de/˜waldmann/ autotool.html – gerade, montags, 7:30–11:00, Li108 – oder gerade, freitags, 9:30–12:45, HB202 Nach Ankündigung Seminare im Computer-Pool (voraussichtlich ab 11. November) • regelmäßig: kleinere Denk- und Programmier-Aufgaben, Kontrolle mündlich im Seminar/Pool oder automatisch (Web-Interface, siehe oben). • schließlich: Prüfungs-Klausur Was ist Informatik (vgl. Kapitel 1 aus Horn/Kerner/Forbig) Wissenschaft von der Informationsverarbeitung junge Wissenschaft mit alten Inhalten (Tabelle S. 21) drei Typen von Maschinen (Tabelle S. 22) Defn. Algorithmus (Seite 22) Computer als Werkzeug (Seite 27f.) Beispiel: Simulation (Wettervorhersage) Geschichte (HKF 1.4) Entwicklung der Mathematik (irdische und astronomische Geometrie, Messungen von Weg und Zeit) Rechenvorschriften (schriftl. Multiplikation), Tafeln von Logarithmen, Winkelfunktionen maschinelle Hilfen bei deren Berechnung (Babbage), programmgesteuerte Webstühle (Jacquard), Lochkartenzählmaschinen frei programmierbare Maschinen (Zuse ab 1936) prinzipielle Grenzen von Algorithmen (Gödel 1931, Turing 1936) Computer-Architektur nach von Neumann frei adressierbarer Hauptspeicher, zur Erleichterung verwende nicht Adressen, sondern Namen. Programm-zustand ist Speicherinhalt (= Abbildung von Adressen auf Werte) Und: Programm steht selbst im Speicher. • Zuweisungen: Name := Ausdruck • wobei Ausdruck: Konstante oder Name oder Operator mit Argumenten • wobei Argument: Konstante oder Name • Sprünge (unbedingte und bedingte) Übung: was ändert sich, wenn man als Argument auch Ausdrücke gestattet? Strukturiertes Programmieren Programm-Ablauf-Steuerung nicht durch Sprünge, sondern durch hierarchische Struktur. • Blöcke (Anweisungsfolgen) • Verzweigungen (if then else) • Wiederholungen (while) • Unterprogramme (benannte Blöcke) Jeder dieser Bausteine hat genau einen Eingang und genau einen Ausgang. Einfache (?) Algorithmen a := a + b ; b := a - b ; a := a - b; Collatz-Problem: Hält dieser Algorithmus für jede Eingabe ≥ 1? Eingabe x; solange (x > 1) wenn x eine gerade Zahl ist, dann x := x / 2 sonst x := 3 x + 1 Übung: finden Sie Startwerte, für die der Algorithmus ziemlich lange rechnet. Sortier-Algorithmen Eingabe: eine Folge (x1, x2, . . . , xn) von Zahlen. Ausgabe: eine Folge (y1, y2, . . . , yn) von Zahlen. Bedingungen: • die Ausgabe ist eine Permutaion (= Umordnung) der Eingabe. • die Ausgabe ist aufsteigend geordnet. Einfacher Algorithmus: Sortieren durch Einfügen. In Seminaren mehr dazu. Sortiernetze Algorithmen “in Hardware”: Komparatoren. Bubblesort-Netzwerk. In Seminaren: Korrektheit. Aufgabe: Finde Sortier-Netzwerk für 4 Elemente mit 5 Komparatoren. Wieviele brauchen Sie für 5? In Seminaren: Odd-Even-Sort für 8. danach Übung: wie geht das für 16? Warum stimmt es? Seminare Wiederholung zur Vorlesung (Defn. Informatik, Algorithmus) Einfache Sortiernetze: 4 Eingänge mit 6, mit 5 Komparatoren. Beweis, daß 4 Komparatoren nicht ausreichen. Dazu: Anzahl der Permutationen von n Elementen ausrechnen. Schubfachschluß wiederholen. Später: Sortiernetze für 5 (6,7,8,9) Eingänge als autotool-Aufgabe. Bubblesort als Netz, induktive Definition. → als Programm. Geschachtelte Schleifen. Ausführung simulieren. Sortieren mit möglichst wenig Vergleichen (5 Elem. mit 7 Vgl., weniger geht nicht) Median von 5 mit 6 Vergleichen (d. h. weniger als Sortieren) Berechenbarkeit, Komplexität (24. 10. 03) Literatur: Horn/Kerner Kap. 8.3 (Komplexität), Kap. 8.2 (Berechenbarkeit) Komplexität von Algorithmen Wie gut ist ein Algorithmus? • Wieviel Rechenzeit/Speicherplatz benötigt er? • Für eine spezielle Eingabe? — Besser: Mengen von ähnlichen (= gleich großen) Eingaben betrachten. • Resourcenverbrauch im besten, mittleren, schlechtesten Fall. • Betrachten der Verbrauchsfunktion (bildet Eingabegröße auf Kosten ab). • Maschinen-unabhängige Aussagen durch Betrachtung des (asymptotischen) Wachstums, d. h. ignoriere: – Unregelmäßigkeiten für kleine Eingaben – konstante Faktoren (für alle Eingaben) Komplexität – Beispiel Sortieren durch lineares Einfügen (Bubblesort, bzw. entsprechendes Netzwerk) benötigt für n Elemente 0 + 1 + 2 + . . . + (n − 1) = (n − 1)n/2 Vergleiche. Egal, auf welchem Rechner wir das ausführen, die Laufzeit wird immer eine quadratische Funktion der Eingabegröße sein. D. h. Eingabegröße verdoppeln → vierfache Laufzeit, verdreifachen → neunfache, usw. Schnelleren Prozessor kaufen lohnt sich kaum, man gewinnt damit nur einen konstanten Faktor. Viel wirksamer sind bessere Algorithmen! Merge-Sort (Sort) sortiere (a) = wenn Länge (a) <= 1, dann gib a aus, sonst setze b = (ungefähr) die Hälfte der Elemente von a; c = die restlichen Elemente von a; b’ = sortiere (b) c’ = sortiere (c); füge b’ und c’ zusammen; die Ausgabe von sortiere(a) enthält alle Element von a genau einmal und ist aufsteigend geordnet. Entwurfsprinzip: Rekursion, Teile und Herrsche (divide and conquer) Merge-Sort (Merge) b’ und c’ sind Listen von Elementen (Zahlen) füge b’ und c’ zusammen = solange ( b’ nicht leer und c’ nicht leer wenn erstes (b’) < erstes (c’) dann ausgabe(erstes (b’)); verkürze b sonst ausgabe(erstes (c’)); verkürze c gib restliche Elemente von b’ aus gib restliche Elemente von c’ aus in der Ausgabe stehen alle Element von b’ und c’ genau einmal. Der Algorithmus erfüllt einen Vertrag: wenn b’ und c’ zu Beginn aufsteigend geordnet sind, dann ist die Ausgabe aufsteigend geordnet. Anzahl der Vergleiche ist ≤ Länge von b + Länge von c − 1 Merge-Sort (Komplexität) Anzahl der Vergleiche? T (1) = 0, T (n) = T (bn/2c) + T (dn/2e) + (n − 1) Beispiele: n 1 2 3 4 5 6 7 8 9 T (n) 0 1 3 5 8 11 14 17 21 Übung: beweise durch Induktion T (2k ) = 1 + (k − 1) · 2k . Mit n = 2k , also k = log2 n folgt T (n) ≈ n log2 n. D. h. Merge-Sort ist asymptotisch besser als Bubble-Sort. Komplexität von Problemen Wie schwer ist ein (algorithmisches) Problem? Wieviel Ressourcen braucht jeder Algorithmus, der das Problem löst? (Bereits gezeigt) Für jedes Sortierververfahren für n Elemente gibt es eine Eingabe, für die das Verfahren ≥ log2(n!) Vergleiche ausführt. Übung: log2(n!) ≈ n log n Folgerung: Sortieren hat eine Komplexität von wenigstens n log n. (D. h. mehr als linear.) Folgerung: Merge-Sort ist optimal (Bubble-Sort nicht). Schwere Probleme Es gibt Aufgaben, deren Lösung exponentiell lang ist. Beispiel: Türme von Hanoi. n Scheiben mit Durchmessern 1, 2, . . . , n liegen übereinander auf einem Turm A (in einem Kloster in Hanoi, jedenfalls der Sage nach). Sie sollen einzeln auf einen Turm B bewegt werden, allerdings darf nie eine größere über einer kleineren Scheibe liegen. Es darf ein Hilfsturm C benutzt werden. Scheibe n (die größte) muß sich irgendwann einmal bewegen (da sie anfangs auf A liegt, aber schließlich auf B sein muß). Im besten Fall bewegt sie sich genau einmal. Zu diesem Zeitpunkt müssen alle anderen Scheiben auf C liegen! Wie kommen sie dorthin? Das ist eine (um eine Scheibe) verkleinerte Version der selben Aufgabe! Hanoi (2) Also ist das die optimale Lösung: move (k, v, n, h) = -- hier gilt: Scheiben 1, 2 .. k -- liegen oben auf v (andere Scheiben eg falls (k > 0), dann move (k-1, von, hilf, nach) lege Scheibe k von v nach n move (k-1, hilf, von, nach) -- hier gilt: Scheiben 1, 2 .. k - liegen oben auf n -(andere Scheiben wurden nicht bew und für k Scheiben braucht man 2k − 1 Züge. n 5 10 15 20 25 30 50 100 H(n) 31 1023 104 106 107 109 1015 1030 Suchprobleme Viele Aufgaben lassen sich als Such-Probleme formulieren. Beispiel 1 (COL): gegeben sind ein Netzplan (ein Graph, bestehend aus Knoten und Kanten) sowie eine Zahl c von Farben. gesucht ist eine Färbung der Knoten, so daß keine Kante zwischen gleichfarbigen Knoten verläuft. (nicht in Vorlesung), Beispiel 2 (Lunar Lockout, siehe www.binaryarts.com): gegeben ist eine Anordnung von Robotern. Gesucht ist eine Folge von (erlaubten) Bewegungen, nach der schließlich der rote Roboter in der Mitte steht. Suchprobleme (2) Bei COL und LL sind die Suchräume beschränkt: es gibt nur |Knoten||Farben| verschiedene Färbungen, bzw. nur |Spielfelder||Roboter| verschiedene Anordnungen. Schlimmstenfalls probiert man alles durch. Der Unterschied ist jedoch, daß man für eine Färbung sehr schnell (in polynomieller Zeit) prüfen kann, ob sie eine Lösung ist; aber eine Anordnung von Robotern allein noch keine Lösung ist; man muß alle Folgen untersuchen. Da nur wiederholungsfreie Folgen interessant sind, sind es trotzdem nur endlich viele. Suchprobleme (3) Beispiel 3 (PCP): Gegeben ist eine Liste von Wort-Paaren. Gesucht ist eine Folge (von Kopien dieser Paare), bei der die Verkettung alle linken Seiten das gleiche Wort ergibt wie die Verkettung aller rechten Seiten. 110 0 1 001 0 1 Beispiele: Das linke hat 1 1 01 0 1 001 kürzeste Lösung AACBCCBC. Aufgabe: finde Lösung für das rechte (es gibt eine). Jetzt ist der Suchraum (alle Folgen) gar nicht beschränkt (die Folgen können beliebig lang sein). Tatsächlich ist PCP algorithmisch unlösbar ! (Es gibt keinen Algorithmus, der für jede Liste von Paaren korrekt entscheidet, ob die Aufgabe eine Lösungsfolge besitzt.) Algorithmisch unlösbare Probleme (1) Da Algorithmen beschreibungs-endlich sind, können wir sie auch durchnumerieren. Z. B. Java-Quelltexte erst der Länge nach und innerhalb der Länge alphabetisch. Die syntax- und typ-fehlerbehafteten Texte streichen wir, übrig bleibt eine Anordnung P0, P1, . . . aller tatsächlich kompilierund ausführbaren Programm-Texte. Algorithmisch unlösbare Probleme (2) Das Halteproblem ist: • Eingabe: ein Zahl x und eine Zahl y • Frage: hält das Programm Px bei Eingabe y? Wir beweisen, daß das algorithmisch unlösbar ist: es gibt keinen Algorithmus, der die Frage für alle Eingaben korrekt beantwortet. Algorithmisch unlösbare Probleme (3) Indirekter Beweis: Falls das Halteproblem doch algorithmisch lösbar ist: Dann definieren wir das Programm H : x 7→ falls Px(x) (Das Programm mit Nummer x, gestartet mit Eingabe x) hält, dann irgendeine nicht haltende Rechnung (Schleife), sonst 0. Das Programm H hat auch eine Nummer, diese sei n. Also H = Pn . Hält H(n)? Wegen der Fallunterscheidung gilt H(n) hält ⇐⇒ H(n) = 0 ⇐⇒ Pn(n) hält nicht ⇐⇒ H(n) hält nicht! Das ist ein Widerspruch, d. h. (wenigstens) eine Annahme war falsch: H ist gar nicht programmierbar! Vergleich von Problemen Man kann Schwierigkeit von Problemen untereinander vergleichen. Definition: P1 ≤ P2 falls es eine (wenig aufwendige) Funktion f gibt, so daß für alle x gilt: x ist Lösung von P1 ⇐⇒ f (x) ist Lösung von P2. Wenn P1 ≤ P2 und P2 (leicht) lösbar, dann auch P1 (leicht) lösbar. Man kann zeigen: Halteproblem ≤ PCP. Nach Kontraposition (Umkehrschluß) folgt: Halteproblem algorithmisch unlösbar ⇒ PCP algorithmisch unlösbar. Indiz: sehr kleine und trotzdem schwere PCP-Instanzen (d. h. mit sehr langer Lösung). Grundlagen der Java-Programmierung (Seminar 7. 11. 03) Objekte objektorientierte Spache Java. Ein Objekt besitzt Attribute und ist Instanz einer Klasse. Eine Klasse beschreibt eine Gesamtheit von Objekten durch Deklarationen von Attributen und Methoden, das sind Unterprogramme. Ein Software-Projekt besteht aus einer Gesamtheit von Klassen. Programmieren mit BlueJ wir benutzen Integrierte Entwicklungsumgebung BlueJ, http://www.bluej.org enthält Projekt-Manager, Editor, (Verweis auf) Compiler, Debugger, Objekt-Inspektor. (d. h. JDK braucht man extra, http://java.sun.com) Wir beginnen mit einfachen Übungen zu Programmstrukturen und ignorieren dafür zunächst alles, was speziell mit Objektorientierung zu tun hat. (für die Auskenner: deswegen sind zunächst alle unsere Methoden static.) Einfach anfangen • BlueJ-Symbol (Vogelkopf) clicken, JDK suchen • Project -¿ New Project (Namen aussuchen) • New Class (Namen aussuchen, z. B. Maxi) • Doppel?Click auf Maxi-Symbol, Editor geht auf • neue Methode eingeben static int maximum (int a, int b) { if ( a > b ) { return a; } else { return b; } } • Compile • Rechts-Click auf Maxi-Symbol, Methode maximum auswählen • zwei Parameter eingeben, Resultat ansehen. Aufgabe: Median implementiere eine Methode static int median (int a, int b, int c) {... } sie soll das mittlere der drei Argumente zurückgeben. Benutzen Sie nur (geschachtelte) Verzweigungen und return (keine Zuweisungen). Zusatz: implementieren Sie Median von fünf Elementen. Sie dürfen Verzweigungen und Zuweisungen benutzen (jedoch keine Schleifen). Programm für Türme von Hanoi Vgl. Vorlesung. Implementiere die Prozedur move! static void move (int k, int von, int nach, int hilf) { . Um die Bewegung auszugeben, benutzen Sie System.out.println ("Scheibe " + k + " von " + von + " nach " + nach ); Bastelaufgabe: Wie schnell geht es, wenn man nicht einen, sondern zwei Hilfstürme benutzen darf? Wie sieht der optimale Algorithmus aus? Wieviele Züge brauchen sie für 5,7,10 Scheiben? (evtl. autotool.) Die Collatz-Folge (Siehe Vorlesung.) Schreiben Sie eine Methode, die für gegebenen Startwert die Folge berechnet und druckt sowie die Länge der Folge zurückgibt. Den Rest bei Division durch 2 bestimmen Sie so: x % 2, den (abgerundeten) Quotienten so: x / 2. static int count (int x) { int c = 0; while ( ... ) { System.out.print (x + " "); if ( ... ) { ... } else { ... } } return c; } Collatz (2) Zusatz: Rufen Sie diese Methode wiederholt auf (für Argumente 1,2,3,. . . ) und geben Sie jeweils neue Rekord-Folgen-Längen aus. static void tops () { int top = 0; for (int x=0; x < 1000000 ; x++) { .. } Grundlagen der Java-Programmierung (Vorlesung 7. 11.) Dokumenationen und Anleitungen zu Java finden Sie hier: http://developer.java.sun.com/developer/ infodocs/ Typen und Ausdrücke Datentypen Literatur: Vergleiche HKF Kapitel 4.3 Nach dem von-Neumann-Modell besteht der Speicher aus Zellen mit jeweils Adresse und Wert. In einer typisierten Sprache gehört jeder Wert (Zellen-Inhalt) zu einem Datentyp. Ein Datentyp besteht aus: • Bereich der zugelassenen Werte • Beschreibung der zugelassenen Operationen (Unterprogramme) Arten von Datentypen • elementare Typen: – (nicht zu große) ganze Zahlen: int – Zeichen: char – Wahrheitswerte: boolean • zusammengesetzte Typen: – Zeichenketten: String – Felder (Arrays), Objekte/Klassen (später) wo werden Typen definiert? • gehören zur Sprache (int, . . . ) • zu Bibliotheken: Applet, Button, .. • sind selbst definiert (Klassen) Deklarationen von Variablen • Form: Typ, Name, Semikolon, Bsp: int x; • Sichtbarkeit: Name kann verwendet werden . . . – im direkt umgebenden Block nach Deklaration bis } – ebenso in allen Unterblöcken in diesem Bereich – sofern nicht verdeckt durch weiter innen deklarierte gleich benannte Variable Mögliche Fehler: • • • • Deklaration einer Variablen vergessen Variable vor Deklaration benutzt Variable in einem Block mehrfach deklariert unabsichtlich durch innere Deklaration Namen verdeckt Typ-Prüfungen Name bezeichnet zu jeder Zeit einen Wert vom deklarierten Typ. Falls nicht, dann durch Fehler im Programm. So früh wie möglich feststellen! (umso geringer sind die Kosten) • (gar nicht: Zentimeter und Zoll addiert, Mars-Sonde abgestürzt) • vor Ausführen von Operationen (nur typreine Addition) • bereits beim Zuweisen an Variablen Typ prüfen • statische Typ-Prüfung: nicht erst bei Ausführung, sondern vorher (beim Kompilieren) Nutzen: nicht nur Sicherheit, sondern Effizienz (vollständige statische Prüfung: keine dynamische Prüfung nötig) Ausdrücke Ausdrücke beschreiben Werte eines bestimmten Typs. • einfache Ausdrücke: – Konstanten (Zahlen, Zeichen, Strings, Wahrheitswerte) – Namen (als Verweis auf eine Speicherstelle) • zusammengesetzte Ausdrücke: – Funktionsaufruf: Name, Liste von Ausdrücken – Operator-Aufruf: Ausdruck, Operator, Ausdruck Bei zusammengesetzten Ausdrücken müssen die Typen aller Teilausdrücke zusammenpassen (Übungen!). Dazu muß man die Typen den Namen (Variablen) sowie der benutzen Funktionen und Operatoren kennen (Beispiele!) Ausdrücke und Typen – Aufgaben int (operator +) (int x, int y); boolean (operator <) (int x, int y); boolean (operator &&) (boolean x, boolean y) int min2 (int x, int y); int fun (t1 x, t2 y); p < (q < r) (p < q) && (q < r) min2 (min2 (1, a), b) fun (fun (c, d), c) Anweisungen Unterprogramme • Funktion (gibt Wert zurück, ist (Teil eines) Ausdrucks) • Prozedur (gibt keinen Wert zurück, ist Anweisung) Beispiel: public static int quad (int x) { int y; retu Bestandteile: • Kopf: (Eigenschaften), Ergebnistyp, Name, formale Parameter bei Prozedur ist Ergebnistyp void • Körper: ein Block Blöcke Ein Block ist eine (beliebig gemischte) Folge von Deklarationen und Anweisungen, die durch { und } eingeschlossen wird. TIP: wenn Sie eine öffnende Klammer tippen, tippen Sie sofort die entsprechende schließende Klammer dazu, und füllen Sie das Innere später. Anweisungen (elementare) • Zuweisung: Name = Ausdruck • Methoden-Aufruf: Name, Liste von Ausdrücken • Rückkehr aus Prozedur: return • Rückkehr aus Funktion: return Ausdruck Beachte: wird durch Semikolon abgeschlossen. Typen müssen passen (was bedeutet das im Einzelnen)? Anweisungen (zusammengesetzte) • Verzweigung: if ( Ausdruck ) Block ( else Block ) • Schleife: while ( Ausdruck ) Block Ausdrücke müssen vom Typ boolean sein. Für die Blöcke Hinweis zum Klammernschreiben beachten! Klassen (vereinfachte Version, für den Moment) eine Klasse besteht aus Namen und einem Block, in dem nur static-Variablen und static-Methoden deklariert werden dürfen. (D. h. Anweisungen selbst gibt es dort nicht — wohl aber innerhalb der Blöcke, die zu den Methoden gehören). Entwurfsregeln Programm-Design Algorithmus in Unterprogramme zerteilen, die man einzeln verstehen, testen, benutzen kann. Jedes Unterprogramm kommentieren. (Kommentar: von // bis Zeilenende) Welchen Vertrag erfüllt das Unterprogramm? • Welche Eingaben erwartet es, • was ist die Wirkung der Prozedur, • bzw. was der Wert der Funktion? Programm-Layout Jedes Unterprogramm kürzer als eine Bildschirmseite! Struktur des Algorithmus (= Schachtelung der Blöcke) soll optisch deutlich werden. In einem Block: jede Anweisung/Deklaration auf neuer Zeile, in gleicher Spalte beginnend. Bei Unter-Block weiter einrücken. schließende Klammer . . . unter Block-Anfang unter öffnender Klammer: if (a > b) { if (a > b) return a; { } return a; (spart eine Zeile) } Ergänzungen Zählschleifen Typische Schleife: for (int i = a; i < b; i++) { System.out.println (i); } ist äquivalent zu { int i = a; while (i < b) { System.out.println (i); i = i + 1; } } Beachte: Bedingung wird jedesmal ausgewertet, Laufvariable ist nur in der Schleife sichtbar. Felder (Arrays) Feld = Folge von Speicherzellen gleichen Typs, durch Zahlen (Indizes, Offsets) adressiert. • Deklaration/Initialisierung: int [] a = { 4, 1, 5, 2, 3 }; • Zugriff (Lesen/Schreiben): int i = 3; a [i] = a [i + 1]; Vorsicht: Index-Zählung beginnt bei 0. • Länge: a.length Zugriff außerhalb von a [0 .. a.length-1] ist Fehler. Beispiele Bubble-Sort static void swap (int [] a, int x, int y) { // vertausche a[x] mit a[y] int h = a[x]; a[x] = a[y]; a[y]=h; } static void bubble (int [] a, int x, int y) // stellt a[x] <= a[y] her if (a[x] > a[y]) { swap (a, x, y); } } Bubble-Sort (II) static void bubble_sort (int [] a) { // sortiert a aufsteigend for (int i=0; i<a.length; i++) { // füge a[i] in bereits // sortiertes a[0 .. i-1] ein for (int j=i-1; j >= 0; j--) { bubble (a, j, j+1); } } } static void check () { int a [] = { 0,5,3,1,4,2 }; println (a); bubble_sort (a); printl } Quick-Sort Ein weiteres Sortierverfahren nach dem Prinzip teile und herrsche sortiere (a [lo .. hi]) = falls ( lo < hi ), dann permutiere Elemente so, daß es ein m gibt mit: für jedes x in a [lo .. m ] und jedes y in a [m+1 .. hi] gilt x <= y sortiere (a [lo .. m ]); sortiere (a [m+1 .. hi]); Quicksort (II) Wie erreicht man die Verteilung? Wähle ein p und bewege: alle x in a [lo .. hi] mit x < p nach links, alle x in a [lo .. hi] mit x > p nach rechts. Ansatz: int p = a [lo]; int l = lo; int r = hi; while (l < r) { while ( a[l] <= p ) { l++; } while ( a[r] >= p ) { r--; } if (l < r) { swap (a, l, r); } } Aufgabe: finde Fehler und repariere! Quicksort (III) Vorteile: • Wenn p immer nahe beim Median, dann schnelles Sortieren (asymptotisch wie bei Merge-Sort, Θ(n log n)) • Dann sogar (um konstanten Faktor) besser als Merge-Sort, weil Elemente in ihren Bereichen verbleiben. Nachteile: • nur wenige Zeilen, aber viele Fehlermöglichkeiten! • Wenn p schlecht gewählt, dann langsames Sortieren (wie Bubble-Sort, Θ(n2), für sortierte Eingaben) • möglicher Ausweg: p = a [zufällig]; Übung am 14. 11. Übung (14. 11.) Kontrollfragen aus HKF Abschnitt 4.1.6 (Struktur-Elemente von Algorithmen) Lunar Lockout as Suchproblem. Aufgabe: wie groß ist der Suchraum? Wiederholung Merge-Sort evtl. Bemerkungen zu Sortier/Merge-Netzen (autotool-Aufgaben) Quicksort: Korrektheit der inneren Schleife, Laufzeit. Lunar Lockout autotool: Robots-R-{Alex,Fourty} Die Großbuchstaben bezeichnen die Roboter. Die Aufgabe ist, A auf die (anfangs leere) Position a zu bewegen. Roboter fahren geradlinig, ohne Bremse, bis zu Hindernis. B . . . . . . . . G . D . . . . . . . . . . . . . . . . . . . . . . . . . . . . E . . . . a . . . . . . . . . . . . . . . . . . . . . . . . . C . . . . . . . . A . . . . . . . . F http://www.thinkfun.com/buy/games/ lunarLockout.html Ähnliches Spiel: Ricochet Robot (Rasende Roboter) von Alex Randolph, http://sunsite.informatik. rwth-aachen.de/keirat/txt/R/Rasenrob.html Ergänzungen (21. 11. 03) Ergänzungen zu Kontrollstrukturen Literatur: HKF Kapitel 5.4 Schleifen-Invarianten static int power (int b, int e) { int p = 1; int q = b; while (e > 0) { if (1 == e % 2) { p = p * q; } q = q * q; e = e / 2; } return p; } Der Ausdruck A = p · q e ist invariant: seine Werte vor und nach Ausführen eines Schleifenkörpers stimmen überein. Invarianten (II) Beweis: Falls e > 0 und e gerade, dann p0 = p, q 0 = q 2, e0 = e/2, also p0 · q 02 = p · q 2·e/2 = p · q e. Falls e > 0 und e ungerade: Übung. Der Wert von A vor der Schleife ist 1 · be, der Wert von A nach der Schleife ist p · q 0 = p. Invarianten (III) Denken Sie zuerst an die Invariante, und notieren Sie sie (als Kommentar zu Beginn der Schleife). Schreiben Sie dann den Schleifenkörper so, daß die behauptete Invariante tatsächlich invariant ist. Für Sonderformen von Schleifen (do-while, break) ist diese Analyse viel schwieriger, deswegen sollten diese auch nicht ohne besonderen Grund benutzt werden. Mehrfach-Verzweigungen int h = n; if (h == 1) { switch (n) { case (1) : Block-A; { Block-A; break; } } else { if (h == 2) { case (2) : Block-B; { Block-B; break; } default : } else { Block-C; { Block-C; } } } } Beachte: break aus historischen Gründen (C) nötig Vorteile: übersichtlicher und effizienter (Sprungtabellen) Übung: wieso wird rechts neue Variable h benutzt? Weitere Formen von Schleifen Schleife mit nachgestelltem Test: do { B; } while ( A ); Aufgabe: finde äquivalentes Programm mit while-do. Vorzeitiges Verlassen/Wiederholen einer Schleife: while ( B1 ) { A1; if ( B2 ) { break; } A2; if ( B3 ) { continue; } A3; } Aufgabe: finde äquivalente while-Schleife (ohne break, continue). (benutze Hilfsvariable boolean c2, ..) Ergänzungen zu Unterprogrammen Argument-Übergabe für Unterprogramme Deklaration: T1 fun (T2 x) { Block; } Aufruf: .. fun (A1) .. • call-by-value (Übergabe als Wert): vor Block x mit Wert von A1 initialisieren. • call-by-reference (Übergabe als Verweis): A1 bezeichnet ein Objekt, in Block bezeichnet x dasselbe Objekt wie A1. • call-by-name (Übergabe als Name): in Block wird x überall durch A1 ersetzt. Argument-Übergabe (Beispiele) Deklaration: static void p (int x) { print (x); x ++; } Deklaration: static void swap (int [] f, int i, int j) { int h = f [i]; f [i] = f [j]; f [j] = h; } Aufruf: int a = 5; print (a); p (a); print (a); Aufruf: int [] a { 2, print (a swap (a, print (a = 4, 1 }; [0]); 0, 1); [0]); Argument-Übergabe in Java • call-by-value für elementare Typen (int, char, boolean, ..) • call-by-reference für zusammengesetzte Typen (Objekte, einschl. Felder und Strings) Parameter-Übergabe (Eigenschaften) • call-by-value: Vorteil: leicht zu verstehen, zu analysieren Nachteil: Kopieren von zusammenges. Werten teuer • call-by-reference: Vorteil: kein Kopieren nötig Nachteil: nicht-lokale Änderungen schwerer zu verstehen • call-by-name: Vorteil: in einigen Spezialfällen einzig mögliche Lösung Nachteil: nicht effizient (evtl. mehrfache Auswertung), undurchschaubare Effekte (siehe Beispiel) Aufgabe zu Call-by-Name static void tausche (int x, int y) { int h = x; x = y; y = h; } Hat das unter Call-by-Name tatsächlich die gewünschte Wirkung? Nein! Betrachte int a [] = { 3, 1, 4, 6 }; int i = 2; tausche (i, a [i]); Übung 24. 11. Sortieren/Aufgaben Bubble-Sort: Code vervollständigen, ausprobieren Quick-Sort: Code vervollständigen, ausprobieren http: //www.imn.htwk-leipzig.de/˜waldmann/ws03/ informatik/programme/sortier/Sortier.java BlueJ-Debugger Kompilieren, im Editor-Fenster Breakpoint setzen (Control-B), im Projekt-Fenster Methode aufrufen, bei Erreichen des Breakpoints erscheint Debugger-Fenster. Step: nächste Zeile ausführen (d. h. Ausführung von Unterprogrammen nicht anzeigen), Step Into: Unterprogramme auch anzeigen. Aktuelle Zeile ist im Editor-Fenster markiert. Objekt-Orientiertes Programmieren (21. 11. 03) Literatur: HKF Kapitel 5.4 (ab S. 291) Objekte, Methoden Daten sind passiv (man kann sie nur lesen/schreiben). Operationen mit Daten durch Unterprogramme. Entwurfsziel: Daten und passende Operationen verbinden. Aus Daten werden Objekte: Definition: ein Objekt besteht aus • Attributen (Daten) • Methoden (Unterprogrammen) Klassen Definition: Eine Klasse beschreibt gleichartige Objekte (gleiche Namen und Typen für Attribute und Methoden). Definition einer Klasse: Objekt deklarieren, initiapublic class Counter lisieren, Methoden aufru{ fen, Attribut lesen: int ticks; { Counter c = void reset () new Counter (); { ticks = 0; } c.reset (); c.step void step () System.out.println { ticks ++ ; } (c.ticks); } } • Attribut: Objektname . A.-Name • Methoden-Aufruf: Objektname . M.-Name Argumentliste Objekte in Bluej Rechts-Klick auf Klassen-Symbol zeigt Konstruktor new Name (). Klick darauf legt neues Objekt an, erscheint als (rotes) Symbol. Rechts-Klick auf Objekt-Symbol zeigt Methoden (Click auf Methode ruft auf). Click auf inspect zeigt Attribute. Lebenslauf eines Objekts • (Deklaration) Counter c; • Konstruktion/Initialisierung c = new Counter (); • Leben “ ” • Finalisierung (erfolgt automatisch) Ein Objekt wird durch Aufruf eines Konstruktors hergestellt (Form: new Klassenname); Dabei Attribute initialisiert (falls so deklariert) (int click = 0;) Konstruktoren Eigene Konstruktoren sind möglich (auch überladene). public Counter (int x) { click = x; } ... Counter c = new Counter (3); Konstruktor-Methode ist Funktion ohne Namen, Ergebnistyp ist die Klasse. Überladen von Namen Ein einziger Name kann verschiedene Methoden bezeichnen, falls man sie anhand der Argumentliste (Länge und Typen) unterscheiden kann. public class C { void p () { ... } void p (int x) { ... } } Beachte: Überladung mit gleichen Argument-Typen und verschiedenen Ergebnis-Typen ist nicht erlaubt. Aufgaben: kann man Prozedur durch Funktion überladen? Methode durch Attribut? Statik und Dynamik solange nichts anderes deklariert ist, gehören jedes Attribut und jede Methoden zu einem Objekt: • jedes Objekt hat seinen eigenen Speicherplatz für Attribute • Benutzung/Aufruf ist nur über Objektnamen möglich Durch Deklaration static: Attribute/Methoden gehören zur Klasse (und nicht zu einem einzelnem Objekt). Benutzung über Klassen- (nicht: Objekt-)Namen. Bsp: int x = Integer.parseInt ("123"); In statischen Methoden sind nur statische Attribute und Methoden benutzbar (warum?) Zeichenketten vordefinierte Klasse String String s = "foobar"; int l = s.length (); // 6 char c = s.charAt (3); // ’b’ String t = s.substring (1, 3); // "oob" Bemerkungen: • Jedes Objekt besitzt Methode String toString (). • Operator + erzeugt Verkettung von zwei Strings. • Wenn ein nur ein Argument von + ein String ist, dann wird auf den anderen toString() angewendet. Beispiel: System.out.println ("x = " + x); Strings (Aufgabe) implementieren Sie einen Test static boolean palindrom (String s) so daß palindrom ("reliefpfeiler") = true. Benutzen Sie s.length(), s.charAt(..), while. Suchen Sie damit eine Zahl n, die keine Palindrom ist, aber deren Quadrat doch. Bsp: 7986442 = 637832238736. (Gibt es unendlich viele solche Zahlen?) (desgl. für dritte Potenz) Hinweis: benutzen Sie nicht int n, sondern long n, sowie Long.toString (n). Weitere Aufgabe zu Palindromen Hält das folgende Programm immer? while ( x ist kein Palindrom ) { x = x + Spiegelzahl von x; } Beispiel: 199, 1190, 2101, 3113. Überprüfen Sie alle Start-Zahlen ≤ 1000. Klassen, Vererbung, Interfaces (5. 12.) Beziehungen zwischen Klassen class C { D ist abgeleitet von C int a; (oder: D erweitert C) void m () { ... } D besitzt } • alle Attribute und Methoden class D extends C { von D int b; void p () { ... } • und weitere, eigene. } Beispiele: • Basis: Zähler mit step, abgeleitet: . . . und reset • Basis: Grafik-Element, abgeleitet: . . . mit Farbe Grundsatz: überall, wo Objekte der Basisklasse stehen, dürfen auch Objekte davon abgeleiteter Klassen stehen. Überschreiben von Methoden Abgeleitete Klassen können Methoden der Basisklasse neu implementieren (überschreiben). class C { class D extends C { int a; void m () { ... } void m () { ... } } } Es wird immer die speziellste Methode benutzt: C x; x.m (); D y; y.m (); Wichtig: das Überschreiben nicht verwechseln mit dem Überladen! Objektorientierung (Überblick) OO = • Objekte mit Attributen und Methoden, • Beziehungen: Vererben und Überschreiben. • Simula 68 (Ole-Johan Dahl, Kristen Nygard) (Prozess-Simulation) Coroutinen, Klassen, Objekte • Smalltalk http://www.smalltalk.org, (Adele Goldberg, Alan Kay, Xerox Parc, ab 1972) (Grafische Nutzeroberflächen) • C with Classes, C++ (Bjarne Stroustrup, ab 1980) • Java (James Gosling, 1995) http://java.sun.com/ features/1998/05/birthday.html (Grafik, Kommunikation, für mobile Endgeräte) Objektorientierte Analyse/Modellierung nicht nur Programme, sondern (technische) Systeme beschreiben: • Komponenten (Objekte), • Eigenschaften (Attribute, Methoden) • Gemeinsamkeiten (Klassen) • Hierarchien (Vererbung) dafür gibt es standardisierte Verfahren, Werkzeuge und Sprachen (UML). automatische Unterstützung von OO-Programmierung durch Generierung von Programmtexten (-Bausteinen). Vererbung: Vor/Nachteile • Vorteil: Nachnutzung von Programmen • Nachteil: abgeleitete Klasse sieht alle Details der Basisklasse verletzt das Prinzip des information hiding: so wenig wie nötig interne Klassen-Informationen nach außen geben, damit nachträglich Implementierung verbessert oder ausgetauscht werden kann. Für größere Projekte: Modularisierung und Information Hiding durch andere Techniken erzwingen (OO kann das gar nicht alles leisten) Abstrakte Methoden und Klassen abstract class C { void p () { .. } ; abstract void m (); } class D extents C { void m () { .. } } abstrakte Methoden sind in Basisklasse deklariert, (aber nicht implementiert), müssen in abgeleiteten Klassen implementiert werden. Basisklasse muß als abstract deklariert werden. Schnittstellen (Interfaces) Schnitstelle = Gesamtheit von abstrakten Methoden interface C { void m (); } Klassen können Schnittstellen implementieren: class D implements C { void m () { .. } } Eine Klasse implementiert mehrere Schnittstellen: class D implements C1, C2 { void m1 () { .. } ; void m2 () { .. } } Beachte: in Java keine Mehrfach-Vererbung (extends) Bsp: Interfaces und Standard-Klassen interface Collection { interface Iterator boolean add (Object o); boolean hasNext Iterator iterator (); Object next (); } } class TreeSet interface Set implementes Set extends Collection { } String [] a = { "eins", "zwei", "drei", "vie TreeSet t; for (int k = 0; k < a.length; k++ ) { t.add (a [k]); } Iteraror it = t.iterator (); while (it.hasNext()) { System.out.print (it.next() + " "); } Abstrakte Datentypen trenne • Aufgabenstellung (Bsp: eine Menge von Objekten verwalten) • von Lösung (Bsp: Verwaltung in geordnetem Baum) Aufgabenstellung ⇒ Interface, Lösung ⇒ Klasse. andere Sprechweise: • Interface ⇒ abstrakter Datentyp, • Klasse ⇒ konkreter Datentyp. viele viele nützliche abstrakte und konkrete Typen: http://java.sun.com/j2se/1.4.2/docs/api/ Applet-Programmierung Applets (Literatur: HKF ab S. 299) Applet: in Webseite eingebettetes Programm Ausführung im Web-Browser (zum Testen im Applet-Viewer). import java.applet.Applet; import java.awt.*; public class Counter extends Applet { Label lab; Button inc; public void init () { lab = new Label ("0"); inc = new Button ("inc"); add (lab); add (dec); } } Applets mit BlueJ programmieren und testen • Project/New Class/applet (erzeugt Beispiel) • Edit (Beispiel nur ansehen), Compile • rechts-click auf Klassensymbol: Run Applet/Run Applet in appletviewer Ereignis-Behandlung in Applets public class Counter { int status = 0; Label lab = new Label (""); void update () { lab.setText (Integer.toString (status)); } ... public void init () { add (lab); ... } } Ereignis-Behandlung in Applets (II) public class Counter { ... Button inc = new Button ("inc"); class Inc_L implements ActionListener { public void actionPerformed (ActionEvent a status++; update (); } } public void init () { ... add (inc); inc.addActionListener (new Inc_L ()); } } Aufgaben: füge Knöpfe für decrement und reset hinzu. Applets in Webseiten einbetten erzeuge Datei Counter.html: <html> <head> <title>Counter Applet</title> </head> <body> <h1>Counter Applet</h1> <applet code="Counter.class" width=500 height=500 > </applet> </body> </html> (BlueJ: Run Applet/Generate Web Page Only) Datei Counter.class enthält Bytecode, entsteht durch Kompilieren von Counter.java (Quelltext ist zur Applet-Ausführung nicht nötig.) Layout-Manager (19. 12.) GUIs und Layout-Manager Erklärungen und Beispiele: http://java.sun.com/ developer/onlineTraining/GUI/AWTLayoutMgr/ Ein GUI (graphical user interface) enthält mehrere Komponenten (z. B. Labels, Buttons), die in einem Container (z. B. Panel) angeordnet werden: public class Thing extends Applet { public void init () { Button b = new Button ("bar"); add (b); Button f = new Button ("foo"); add (f); } } Explizite Positionierung (pfui) setLayout (null); Button b = new Button ("bar"); b.setBounds (200,300,50,30); add (b); Button f = new Button ("foo"); f.setBounds (100,200,100,40); add (f); Nachteile: • keine Anpassung an variable Rahmengrößen • keine Anpassung an variable Elementgrößen • viel zu viele Zahlen Implizite Positionierung durch Manager (gut) fließende Anordnung (FlowLayout): setLayout (new FlowLayout ()); // ist Defaul for (int k = 0; k < 100; k++) { add (new Button ( "B" + Integer.toString (k) )); } Beachte Wirkung von Window-Resize! Gitter-Anordnung (GridLayout) setLayout (new GridLayout (10,0)); ActionListener (Wiederholung) Label top = new Label ("Top"); int status = 0; void update () { top.setText (Integer.toString (status)); } class Step implements ActionListener { int myself; public Step (int k) { myself = k; } public void actionPerformed (ActionEvent a { status += myself; update (); } } Button b = new Button ( .. ); b.addActionListener (new Step (11)); Manager (II) Rahmen-Anordnung: setLayout (new BorderLayout ()); add (new Button ("Top"), BorderLayout.NORTH) add (new Button ("Foo"), BorderLayout.WEST); add (new Button ("Bar"), BorderLayout.EAST); add (new Button ("Bot"), BorderLayout.SOUTH) add (new Button ("Mid"), BorderLayout.CENTER Hier kann aber jeweils nur ein Element stehen — schade. Container als Elemente von Containern setLayout (new BorderLayout ()); add (new Button ("Top"), BorderLayout.NORTH) add (new Button ("Foo"), BorderLayout.WEST); Panel p = new Panel (); p.setLayout (new GridLayout (10,0)); for (int k = 0; k < 97; k++) { p.add (new Button( "B" + k )); } add (p, BorderLayout.CENTER); (Bevorzugte) Abmessungen Der Typ Dimension beschreibt Rechtecke. (die meisten) Komponenten haben fließende Abmessungen (d. h. können in verschiedenen Größen dargestellt werden). Jede Komponente hat Methoden public Dimension getPreferredSize (); public Dimension getMinimumSize (); public Dimension getMaximumSize (); Ein Layout-Manager kann diese Sizes seiner Komponenten berücksichtigen, . . . und muß selbst die Sizes seines Containers ausrechnen. Management von Abmessungen • FlowLayout: – stellt jede Komponente in preferredSize dar – falls Container fest, dann Zeilenumbrüche – preferred size des Containers: alles in einer Zeile • GridLayout: – Umbrüche nach festgelegter Zeilen- oder Spalten-Zahl GridLayout(z,0) oder GridLayout(0,s) – stellt alle Komponenten gleichgroß dar – bestimmt dazu Maximum aller preferred sizes – verkleinert/vergrößert alles so, daß es paßt – preferredSize: nicht verkleinern BorderLayout und preferred sizes • Nord und Süd: Höhe ist preferred Höhe der Komponente, • West und Ost: Breite ist preferred Breite, • Mitte: was übrigbleibt Aufgabe: finde Darstellung (Formel) für preferredSize des Containers aus preferredSizes der fünf Komponenten! Tip: oft hilft BorderLayout mit nur zwei oder drei Komponenten. Aufgabe: wann ist FlowLayout innerhalb anderer Container sinnvoll? (selten!) Layout (Zusammenfassung) • Durch geeignete Schachtelung von Containern (Panels) • und jeweils geeignete Manager • lassen sich alle vernünftigen Layout-Aufgaben lösen, • ohne auch nur eine einzige explizite Koordinate anzugeben. Für GUI-Entwurf: benutze Skizze (Zeichnung): • gegenseitige Lage der Komponenten (Rechtecke) • Verhalten bei Resize (Pfeile) Gerechte Kuchen-Teilung Gerechte Kuchen-Teilung Jack Robertson and William Webb: Cake–Cutting Algorithms, http://www.akpeters.com/book.asp?bID=48 Beispiele: http://www.informatik.uni-leipzig. de/˜joe/edu/ws01/xmas/ Programmier-Übungen (Januar) Übung Applets Quelltext: http://www.imn.htwk-leipzig.de/˜waldmann/ ws03/informatik/programme/counter/ • kopieren, in BlueJ öffnen, kompilieren, ausprobieren. • Fügen Sie einen Knopf dec (nach unten zählen) hinzu • . . . reset (auf 0 setzen) • Ändern sie Label auf TextField • Fügen Sie Knopf set hinzu (soll Zähler auf aktuellen Inhalt des Textfeldes setzen) Hinweis: Button set = new Button ("set"); class Set_Listener implements ActionLi public void actionPerformed (Actio String s = ??? status = Integer.parseInt (s) update (); } } Übung Datenstrukturen Quelltext: http://www.imn.htwk-leipzig.de/˜waldmann/ ws03/informatik/programme/robots.zip 1. Kopieren, auspacken, in BlueJ öffnen (mit “open non Bluej”), ausprobieren. 2. Wie werden Bewegungen implementiert? (State.java) 3. Wozu wird (Tree)Map verwendet? (Was ist Unterschied zwischen Map und TreeMap? Benutze Java-Dokumentation.) 4. ersetzen Sie TreeMap durch HashMap. 5. Erklären Sie eine Verwendung eines Iterators (Im Zusammenhang mit Map und Set). 6. Implementieren Sie einen Test auf Erreichen des Spielziels: neues Label, das anzeigt (“Ja”/”Nein”), ob alle im Ziel sind. Hinweis: ... { Set s = goals.keySet (); Iterator i = s.iterator (); while (...) { String name = i.next (); Point soll = (Point) goals.get (name); Point ist = ... if (! soll.equals (ist)) ... } } 7. Wie funktioniert der “Undo”-Button? (Hinweis: Stacks) 8. Implementieren Sie die Anzeige der Zugfolgen (am besten gleich in der Form, die für das autotool paßt). Datenstrukturen Listen Folge von Elementen [y0, y1, . . . , yn−1] Methoden: • Einfügen: void add (int i, Object o): aus Liste [y0, y1, . . . , yi−1, yi, . . . , yn−1] wird Liste [y0, y1, . . . , yi−1, o, yi, . . . , yn−1]. • Lesen: Object get (int i): Liste ist (und bleibt) [y0, y1, . . . , yn−1], Resultat ist yi. • Entfernen: Object remove (int i): aus Liste [y0, y1, . . . , yi−1, yi, yi+1, . . . , yn−1] wird Liste [y0, y1, . . . , yi−1, yi+1, . . . , yn−1], Resultat ist yi • testen: size, isEmpty (Deklarationen?) Beachte: bei add und remove ändern sich die Indizes der Elemente auf bzw. nach i. Wo ist der Konstruktor? Hinweis: List ist keine class, sondern . . . Bäume Hierarchisch angeordnete Sammlung von Knoten Jeder Knoten hat • einen Schlüssel (Wert, Inhalt) (ein Objekt) • eine Liste von Knoten (den Kindern) Beispiele: Verwaltungen, HTML-Dokumente, biologische Taxonomien, Menüs bei AV-Geräten, Bezeichnungen: • Wurzel: der Knoten, der kein Kind ist • Blatt: Knoten mit leere Kinderliste • innerer Knoten: kein Blatt. Binäre Bäume binärer Baum: jeder innere Knoten hat geanu zwei Kinder (links, rechts) vollständiger binärer Baum: alle Blätter gleich weit von Wurzel entfernt. (Pfadlänge = Anzahl der Verbindungen = Höhe des Baumes.) Wieviele Knoten hat ein vollständiger binärer Baum der Höhe h? Kellerspeicher (Stacks) ein Keller is eine Folge von Elementen [y1, y2, . . . , yn] Zugriffe (Lesen, Schreiben) sind links! Operationen: • Stack () Konstruktor, erzeugt leeren Keller [] • einkellern: void push (Object o): aus Keller [y1, y2, . . . , yn] wird Keller [o, y1, y2, . . . , yn] • ansehen: Object peek (): Keller ist (und bleibt) [y1, y2, . . . , yn], Resultat ist y1. • auskellern: Object pop (): aus Keller [y1, y2, . . . , yn] wird Keller [y2, . . . , yn], Resultat ist y1. • testen: boolean empty (): is Keller leer, d. h. gleich []? Implementiere peek() durch die anderen Methoden. Wie kann man einen Keller kopieren (nur unter Benutzung der angegebenen Methoden)? Baum-Durchquerungen Beispiel: Ausgabe von Operator-Ausdrücken: void print (Knoten t) { if t ist Blatt { print (t.key); } else { print ( t.left ); print ( t.key ); print ( t.right ); } } Beispiel: Auswertung von Operator-Ausdrücken: int wert (Knoten t) { if t ist Blatt { return t.eintrag; } else { int l = wert ( t.links ); int r = wert ( t.rechts ); return (l ‘t.key’ r); } } Pre-, In-, Post-Order • pre-order: Wurzel, linker Teilbaum, rechter Teilbaum • in-order: linker Teilbaum, Wurzel, rechter Teilbaum • post-order: linker Teilbaum, rechter Teilbaum, Wurzel Ordne zu: Operator-Ausdruck drucken, Türme von Hanoi, Operator-Ausdruck auswerten, Erlaß einer Regierung bekanntgeben/umsetzen (autotool) Rekonstruiere den binären Baum aus: pre-order [5, 1, 7, 0, 9, 8, 2, 4, 3], in-order [7, 1, 0, 5, 2, 8, 4, 9, 3] Durchquerung ohne Rekursion In welcher Reihenfolge werden hier die Knoten besucht? int wert (Knoten t) { Stack s = new Stack (); push (t); while (! s.empty ()) { Knoten x = s.pop (); print ( x.key ); if x ist kein Blatt { s,push (x.right); s.push (x.left); } } } Warteschlangen (Queues) Speicher für Folge [y1, y2, . . . , yn], Schreiben nur links, und Lesen nur rechts. (in Java: als Teil von LinkedList) • LinkedList () Konstruktor, erzeugt [] • schreiben void addFirst (Object o): aus [y1, y2, . . . , yn] wird [o, y1, y2, . . . , yn] • lesen: Object removeLast (): aus [y1, y2, . . . , yn−1, yn] wird [y1, . . . , yn−1], Resultat ist yn. • testen: boolean isEmpty (): ist Schlange leer, d. h. gleich []? Mit der Schlange durch den Baum In welcher Reihenfolge werden hier die Knoten besucht? int wert (Knoten t) { LinkedList s = new LinkedList (); addFirst (t); while (! s.empty ()) { Knoten x = s.removeLast (); print ( x.eintrag ); if x ist kein Blatt { s.addFirst (x.links); s,addFirst (x.rechts); } } } heißt level-order Rekonstruktions-Aufgabe? Suchbäume Ein Suchbaum ist ein binärer Baum, bei dem für jeden inneren Knoten gilt: jeder Schlüssel in t.links ist kleiner als t.key und t.key ist kleiner als jeder Schlüssel in t.rechts Suchbäume benutzt man, um Schlüssel schnell wiederzufinden. Suchen gesuchten Schlüssel mit Schlüssel der Wurzel vergleichen, nach links oder rechts absteigen und weitersuchen. search (Knoten t, Key k) { if ( t.key == k ) { return t; } else { if t ist kein Blatt { if ( k < t.key ) { return search (t.left, k); } else { return search (t.right, k); } } } } Einfügen insert (Knoten t, Key k) { if t ist kein Blatt { if ( k < t.key ) { insert (t.left, k); } else { insert (t.right, k); } } else { if ( k < t.key ) { t.left = new Blatt (k); } else { t.right = new Blatt (k); } } } Löschen? Blatt löschen ist einfach. Wie löscht man einen inneren Knoten? Balancierte Bäume: (2,3)-Bäume (2,3)-Baum: • alle Blätter sind gleich tief und haben keine Schlüssel • jeder innere Knoten ist – ein 2-Knoten: 2 Kinder [l, r], 1 Schlüssel x jeder Schlüssel in l < x < jeder Schlüssel in r – ein 3-Knoten: 3 Kinder [l, m, r], 2 Schlüssel [x, y] jeder Schlüssel in l < x < jeder Schlüssel in m < y < jeder Schlüssel in r Diese Eigeschaften (Invarianten) müssen bei allen Operationen erhalten bleiben. Eigenschaften von (2,3)-Bäumen Ein (2,3)-Baum der Höhe n hat wenigstens 1 + 2 + 22 + . . . + 2n = 2n+1 − 1 Knoten und höchstens 1 + 3 + 32 + . . . + 3n = (3n+1 − 1)/2 Knoten. Aufgabe: wieviele Knoten hat ein (2,3)-Baum der Höhe 10 wenigstens? höchstens? Aufgabe: welche Höhe hat ein (2,3)-Baum mit 106 Knoten wenigsten? höchstens? Einfügen in (2,3)-Bäume (Suchen funktioniert ähnlich wie bei binären Suchbäumen.) • suchen den Knoten (direkt über Blättern), der neuen Schlüssel aufnehmen sollte. • aus 2-Knoten wird 3-Knoten (OK), • aus 3-Knoten wird 4-Knoten (verboten). • Lösung: – diesen 4-Knoten in zwei 2-Knoten (und einen Schlüssel) zerlegen, – diesen Schlüssel in Vorgänger einfügen (a, b)-Bäume Das funktioniert statt (2, 3) genauso für Zahlen (a, b) mit a ≤ (b + 1)/2. (Warum diese Einschränkung?) Für große Datenbanken benutzt man z. B. a = 10, b = 100. ¡¡¡¡¡¡¡ top.tex ======= ¿¿¿¿¿¿¿ 1.4 Software-Technik Ergonomische Software Ziel: angenehm für Benutzer und Programmierer erfordert fachmännisches Vorgehen auf verschiedenen Ebenen: • Gesamt-Projekt • Implementierung im Großen • Programmieren im Kleinen Software-Projekt-Manangement Arbeitsschritte (klassische: nacheinander) • Analyse • Spezifikation • Entwurf • Implementierung • Test • Betrieb Software-Projekt-Manangement (II) beachte auch (in jedem Schritt): • Qualitätssicherung • Kontakt zum Kunden modern“: extreme programming, rapid prototyping, ” refactoring Richtlinien zur Algorithmenkonstruktion (vgl. Horn/Kerner/Forbrig S. 213 f.) • Hierarchische Struktur: – – – – – Algorithmus ist ein Baum, jeder Teilbaum löst ein Teilproblem, in jedem inneren Knoten steht die Spezifikation, seine Kinder sind die Teilschritte der Lösung. Die Blätter sind elementare Operationen. • Top-Down-Entwicklung, schrittweise Verfeinerung Richtlinien (II) • Blockstruktur: – jeder Teilbereich hat genau einen Ein- und einen Ausgang – und erfüllt einen Vertrag (wenn Vorbedingung zutrifft, dann ist nach Ausführung die Nachbedingung wahr) • Lokalität der Daten: Programmbereiche, die bestimmte Daten benutzen, sollten – wenig überlappen – den Kontrollbereichen entsprechen Modulare Programmierung Modul • funkionell abgeschlossener Teil eines Softwareprojektes, • der separat entwickelt wird. modulares Programmieren erlaubt • Anwenden der Richtlinien (top-down usw.) • getrenntes, zeitgleiches Entwickeln • Einsparungen durch Nachnutzen von eigenen und fremden Modulen Modul-Schnittstellen Modul besteht aus • Schnittstelle und • Implementierung Die Nutzer eines Modules (= aufrufende Programme) kennen nur die Schnittstelle. (Lokalität der Daten, Datenabstraktion) Damit kann Implementierung leicht ausgetauscht werden (→ flexibel, portabel) Re-Factoring auch während und nach der Entwicklung immer weiter modularisieren: • Können weitere Module genutzt werden? • Kann Funkionalität in neue Module ausgelagert werden? nach rapid prototyping mit der Axt reingehen“, vornehme ” Bezeichnung: re-factoring. ist geboten bei • überlangen Modul-Quelltexten (d. h. > 1 Bildschirmseite !) • Code-Verdopplungen (verschiedene Programmteile mit ähnlicher Funktion) Module in Java? Hilfsmittel für modulares Programmieren: • Klassen, • Interfaces, • Packages. beachte: Objekt-Orientierung 6= Modularisierung, in Java wurde Objekt/Klassen-Konzept erweitert (Wdhlg: wodurch?), und es muß nun auch Modularisierung ausdrücken. Klassen als Module Klasse beschreibt Objekte (Attribute und Methoden). Dient damit zum Strukturieren und Zusammenfassen von Algorithmen. Wie kann Implementierung versteckt werden? Attribute und Methoden, die als private deklariert sind, können nur innerhalb der Klasse benutzt werden. Damit bilden die nicht-privaten Attribute und Methoden die Schnittstelle der Klasse. Regel: grundsätzlich alle Attribute privat deklarieren, damit jedes Lesen und Schreiben von außen“ bemerkt ” wird und korrekt behandelt werden kann. Interfaces Ein Interface beschreibt Gemeinsamkeiten von Modul-Schnittstellen. andere Sprechweise: • Interface = abstrakter Datentyp = Spezifikation • Klasse = konkreter Datentyp = Implementierung Programmierer entscheidet zunächst, welcher abstrakter Datentyp benötigt wird, und wählt dann einen dazu passenden konkreten Datentyp aus. Beispiel: ADT Menge, konkret: Bitfolge, Liste, Suchbaum. Packages Ein Package ist eine Sammlung von Klassen (und Interfaces). Beispiel: java.applet ist ein Package. Man bezeichnet Methode m der Klasse C aus Package P durch P.C.m Man kann P. weglassen, wenn vorher import P.C stand. import P.* macht alle Klassen aus P sichtbar. Die Schnittstelle eines Packages enthält: alle als public deklarierten Klassen, Attribute und Methoden. Packages in Archiven Interesse an Modularisierung und Daten-Abstraktion nicht nur wegen der schönen Entwurfsprinzipien. Programmtext der Implementierung einer Schnittstelle soll versteckt werden oder ganz entfallen, weil er • zu groß ist • noch verarbeitet (kompiliert) werden muß • geheim bleiben soll Zur korrekten Benutzung eines Modules ist es ausreichend und effizient, wenn die Implementierung kompiliert vorliegt. Kompilierte Klassen eines Packages werden in Package-Archive (P.jar) komprimiert gespeichert. Freie“ Software ” Der naive “ unternehmerische Ansatz ist, ein ” Softwareprojekt zu entwickeln und dann das kompilierte Produkt zu verkaufen oder zu vermieten. Andererseits sind Programme Texte, also Gedanken, und diese sind bekanntlich frei. Die Idee der Freien Software ist es, Software (Quelltexte) grundsätzlich zu veröffentlichen, weil so der größte Nutzen für die Allgemeinheit entsteht. Freie“ Software ” Warum sollte das ein Entwickler/eine Firma tun? Wovon soll er dann leben? Vom Verkauf von Dienstleistungen (Installation, Wartung, Schulung). Damit leben die späteren Dienstleister auf Kosten der früheren Programmierer? Nein, sie schreiben selbst freie Software. Freie“ Software (II) ” Frei sein heißt für Software: • Programmierer bleibt Autor und Urheber • Quelltext ist frei verfügbar, jeder darf ihn nutzen (d. h. lesen, kompilieren, ausführen) • und auch verändern und erweitern • aber alle Änderungen müssen frei bleiben. Siehe auch Free Software Foundation http://fsf.org/, GAOS e.V. Leipzig http://goas.org/. Freie“ Software (II) ” Bekannte und berühmte freie Software-Projekte sind • Emacs (Editor) • GCC (Compiler), ab 197? • GNU (= GNU’s Not Unix): Dienst- und Anwendungsprogramme für ein UNIX-ähnliches Betriebssystem, ab 198? • Linux, ein Betriebssystem-Kern, ab ca. 1990 und vieles andere mehr: (TEX), KDE, Mozilla, gnugo, . . . Free Software Directory: http://www.gnu.org/directory/, Freie Software als Wirtschaftsfaktor für den Anwender ist es natürlich billiger . . . allerdings entstehen Dienstleistungskosten freie Software-Entwicklung ist flexibler (anpassungsfähiger, schneller) nutzt z. B. schnelle Hardware viel besser aus als veraltete Systeme auch große Firmen wollen da mitspielen (und sich street credibility kaufen) IBM unterstützt Linux, Sun gibt Java-Technologie frei Freie Spezifikationen Zur Informatik-Anwendung gehören nicht nur Software, sondern auch Hardware und Protokolle. Wenn deren Spezifikationen aber nicht frei sind, kann dafür niemand Software (Treiber) schreiben, und so bleibt der Hersteller nach einer Weile auf seiner Hardware sitzen, weil sie nur (wenn überhaupt) mit anderen eigenen Produkten kompatibel ist. Davon hängt also viel Geld ab! Die Erkenntnis, daß freie Spezifikationen (Standards) der Gesamtwirtschaft mehr nutzen (als sie Einzelnen schaden) hat sich im wesentlichen durchgesetzt. Bsp: Internet, ISO 9660 (CD-ROM, DVD), IEEE 1394 Wissenschaft, Freiheit, Sicherheit Jede Wissenschaft lebt vom Publizieren von Ideen (und nicht vom Geheimhalten). Nur dadurch können diese geprüft, implementiert und entwickelt werden. Beispiel: Kryptographie, Systemsicherheit. Sollte man kritische “ Algorithmen lieber doch ” geheimhalten? Ganz falsch! Sicher sind nur Verfahren, deren Sicherheit wissenschaftlich bewiesen wurde. Gerade dazu müssen sie veröffentlich werden. Zahlentheorie, Komplexitätstheorie usw. Ales frei = alles gut? wie Prof. J. Winkler http://psc.informatik. uni-jena.de/personen/perso.htm, der bei Siemens die (natürlich unfreien) Compiler für CHILL und Ada gebaut hat, an dieser Stelle zu sagen pflegte: There is no such thing as a free lunch. RTFC Der Bildungs- und Unterhaltungswert freier Quelltexte ist jedenfalls unbestritten. Von UCB (Univ. Calif. Berkeley) wird berichtet, daß dort (in den goldenen 70er Jahren) überhaupt kein Programm installiert werden durfte, ohne gleichzeitig den kompletten Quelltext im gleichen Directory abzulegen. In diesem Sinne . . . RTFC = read the fXXXing code! Zusammenfassung • Informatik und Algorithmen – Geschichte der Informatik – Komplexität von Algorithmen – Sortier-Algorithmen (bubble, merge, quick) • Grundlagen der Programmierung – – – – Daten (einfache, zusammengesetzte) Anweisungen/Kontrollstrukturen Ausdrücke (Ausdrucksbäume) Typen Zusammenfassung (II) • Objektorientiertes Programmieren – – – – Objekte, Methoden, Attribute, Klassen, Interfaces Aggregation, Vererbung Überladen, Überschreiben GUI: Layout-Management, Ereignis-Behandlung • Datenstrukturen – Listen, Stacks, Queues – Bäume, Durchquerungen, Balance • Softwaretechnik – Entwurfsregeln – Module, Schnittstellen – abstrakte und konkrete Datentypen Autotool – Highscore – Auswertung 67 935198 Enrico Unger 57 35202 Markus Unger 45 34985 Tanja Czerwinski 27 . . . Preise (teilweise) gesponsort von http://www.capitospiele.de/ Test-Fragen Test-Fragen Was ist ein Algorithmus? zur Computer-Architektur nach von Neumann: was ist ein Programmzustand? welche zwei elementaren Programmbausteine gibt es? wo steht das Programm? was folgt daraus? Welche Möglichkeiten gibt es beim strukturierten Programmieren, die Abarbeitungsreihenfolge zu beeinflussen? Welche (zwei!) Beziehungen müssen zwischen Eingabe und Ausgabe eines Sortier-Algorithmus gelten? Wie definiert man die Komplexität eines Algorithmus? Nennen Sie die Komplexität von zwei Sortierverfahren. Auf welchem Algorithmen-Entwurfs-Prinzip beruht a) Bubblesort, b) Mergesort? Wie definiert man die Komplexität eines Problems? Nennen Sie ein algorithmisch unlösbares Problem. Welche Gemeinsamkeiten, welche Unterschiede gibt es zwischen Prozeduren und Funktionen? Was ist ein Datentyp? Nennen Sie einige einfache und einige zusammengesetzte Typen. Wann und wie werden Typ-Informationen ausgenutzt? Was ist ein Objekt? Wozu benutzt man Klassen? Wie muß die Methode foo der Klasse C deklariert sein, damit folgender Aufruf korrekt ist: C x = new C (); int y = 4 + x.foo ("bar", 6); Zeichnen Sie den Syntaxbaum für den Ausdruck i < a [ a.length - i ] und beschriften Sie jeden Knoten mit seinem Typ. Wie müssen i und a deklariert sein? Warum werden in Java Objekte nicht als Wert, sondern durch Verweis an Unterprogramme übergeben? (Zwei Gründe.) Wie ist ein Konstruktor für eine Klasse C zu deklarieren, der ein Argument vom Typ int hat? Erläutern Sie die Bezeichnungen Aggregation und Vererbung für Beziehungen zwischen Klassen. Welche Gemeinsamkeiten und Unterscheide gibt es zwischen Überladen und Überschreiben von Methoden? Folgende Deklarationen sind gegeben: class C { int f (int x) { ... } int f (int x, int y) { ... } int g (int z) { ... } int h (int x, C y) { ... } } class D extends C { int f (int x) { ... } } class E { int f (int x) { ... } } Kennzeichnen Sie Überladungen, Überschreibungen. Die folgenden Objekte sind deklariert C c; D d; E e; Sind die folgenden Methoden-Aufrufe korrekt? Falls ja, welches Unterprogramm wird jeweils tatsächlich aufgerufen? (Wir nehmen an, daß die Variablen initialisiert wurden.) c.f (); c.f ("foo"); d.f (4); d.g (5); d.f (4, 6); e.f (4, 6); c.h (8, d); Untersuchen Sie, ob alle Bedingungen bei der Benutzung der static-Annotation erfüllt sind: class C { static int i; int j; static int f (int x) { return i + 2 * j; } int g (int x) { ... } static void check () { System.out.println (f (3) * g (4)); } } Welche Aufgabe hat ein ActionListener, wie wird dieses Interface in einem Applet benutzt? Warum sollte man zum Layout der Komponenten eines GUI einen Manager benutzen? Was ist ein Interface? Welche Beziehungen sind zwischen einem Interface und anderen Programmteilen möglich? Warum geht das nicht: interface Foo { ... } ... Foo x = new Foo (); Sowohl ein Array als auch eine Liste speichert eine Folge von Elementen. Welche Listen-Operation ist für Arrays nicht ausführbar? Sowohl ein Baum als auch eine Liste speichern eine Menge von Elementen (Schlüsseln). Wieso ist ein Baum günstiger, wenn man oft über die Schlüssel auf die Elemente zugreifen muß? Beweis oder Gegenbeispiel: wenn man die Pre-Order-Reihenfolge eines binären Baumes spiegelt, dann erhält man die Post-Order-Reihenfolge. Wie kann man aus einer vorgegebenen Liste von Schlüsseln einen balancierten Suchbaum herstellen? (Hinweis: nicht durch wiederholtes Einfügen)