Infomratik II Inhalt: Korrektheitsnachweis (Invarianten und vollst. Indukt.) Robustes Programmieren mit Ausnahmen (exceptions) Java: C-Level Java-Klassen als Datenstrukturen Dynamische Klassen und Referenzen Bäume Syntaxdiagramme Rekursiver Abstieg Infix, Postfix, Operatorbaum, Stack Codegenerierung, Compiler, Interpreter Java-VM als Bytecode-Interpreter Pakete Klassenhierarchie Polymorphie Abstrakte Klassen Suchbäume, Sortieren Backtracking Spieltheorie, Minimax, AlphaBeta Rekursives Problemlösen (Hanoi, Mergesort) Effizienz, O-Notation Simulation (zeitgesteuert, ereignisgesteuert) Heap, Heapsort Pseudoparallelität Threads in Java Algorithmus in Java: - ägyptisches Multiplikations-Verfahren: a*b: a verdoppeln, b halbieren bis eins; alle a wo b gerade streichen; restliche a aufsummieren. Beweis: Induktion nach b - Exceptions: try { … } catch (Fehlertyp e) { … } Java: - Programmstruktur: - import … importiert Pakete aus anderen Klassen, - Methode: Funktionen oder Prozeduren anderer Sprachen, - Konstruktor: Methode die beim erzeugen der Klasse automatisch aufgerufen wird, - main-Methode: public static void main (String[] args) { … } - static int i im Klassenkörper sind globale Variablen - Integer: byte (8 bit), short (16 bit), int (32 bit), long (64 bit) Gleitkommazahl: float (32 bit), double (64 bit) Zeichen (Unicode): char (16 bit): Literale ‘a’, ‘/’, ‘3’ boolean Literale: true, false String msg = „Hallo“ oder msg = new String(„Hallo“); + zum Veketten; s1.compareTo(s2) Vergleich - Arrays: int [] x; x = new int[7] (Index 0…6!!!) x[0] = 3, x.length gibt Länge; mehrdimensional: float [] [] Matrix = new float [4] [4] - Typenumwandlun: float myFloat = 3.12; int myInt = (int)myFloat; int i = Integer.parseInt(s) von String zu Integer - Hüllenklassen: int x = 5; Integer Huelle = new Integer(x); System.out.println(Huelle.intValue()); - In-/Output: System.in.read() liest einzelnes Zeichen ein (char). System.out.println(„bla“) Output. DataInputStream eingabe = new DataInputStream(System.in); String zeile = ein.readLine(); int Zahl = Integer.parseInt(zeile); (import java.io.* und Exception-Handling nicht vergessen) Java Klassen als Datenstrukturen: - Beispiel: class Datum { private int Tag, Monat, Jahr; (interne Variabeln im Klassenkopf definiert) public Datum(int T, int M, int J) {Tag = T; Monat = M; Jahr = J;} (Konstruktor) public void Ausgeben() { System.out.prinln(Tag + „.“+Monat+“.“+Jahr);} (eine Methode) } - Klassenmethoden werden als static deklariert. (Im Gegensatz zu Instanzenmethoden.) Dynamische Klasen und Referenzen: - Datum Heute = new Datum(…); Heute ist eine Referenzvaribale, mit new wird die dynamische Variable erzeugt - Klasse kann auch „Variablen vom eigenen Typ“ im Konstruktor haben. (Anwendung Listen etc.) - Wenn Heute=Morgen; gemacht wird, zeigen beide auf das selbe Objekt, die dynamische Variable von Morgen geht verloren!!! Syntaxanalyse und Übersetzung: - Bäume: Kanten und Knoten (#Knoten = 1 + #Kanten), je Knoten eine Kante, keine Zyklen o.ä. Blatt = Knoten am Ende des Baumes Darstellungen: Graph (ev. gerichteter), Mengendiagramm, eingerückte Form, - - - - Linksklammerdarstellung. (Siehe S. 66) Binärbäume Æ Arraydarstellung Syntaxdiagramme: „Nicht-Terminalsymbole“ (eckig, = rekursiver Aufruf eines anderen Syntaxdiagramms oder sich selbst) und „Terminalsymbole“ (rund, = Zeichen) Anwendung: Parser, der einen Ausdruck auf Korrektheit überprüft. Symmetrisches traversieren eines Binärbaumes (inorder): 1. linker Unterbaum, 2. sich selbst (Konten), 3. rechter Unterbaum. (Z.b. für Operatorbäume mit *, +, etc.) Postfix: Zuerst die (beiden) Ausdrücke, dann der Operator. (Infix: a+b, Postvix: ab+) Æ postorder traversieren des Binärbaumes : 1. linker Unterbaum, 2. rechter Unterbaum, 3. sich selbst (Knoten) Stack: (S. 82) mittels Array, push (Element drauflegen) und pop (oberstes Element wegnehmen) Postfixumwandlung mittels Stack: Operanden ausgeben, Operatoren in Stack, schliessende Klammer Æ pop Postfixauswerter: Operanden in Stack, Operatoren auf oberste beide Stackelemtene anwenden und Resultat in Stack Æ am Schluss bleibt Resultat im Stack Java-Bytecode: iconst_n (bipush n, wenn gleich für Operation verwendet wird???) (n ein Integer), iconst_m1 (-1), istore_1, iload_1, iadd, imul, ireturn, if_icmpgt 28, goto 29, iand, ior, nop (do nothing) etc. Z.b. a=1+2: iconst_1 iconst_2 iadd istore_0 (a zeigt nun auf die Adresse 0, in welcher der Wert 3 steht.) Pakete in Java: - Paket = zusammengehörige Menge von Klassen. Paket b in Paket a: Paket a.b, Deklaration: package test; - Importieren von Klassen: import java.util.Random oder import java.util.* Æ kann nun mit Random verwendet werden. - Nur eine public class pro Datei Æ in mehrer Dateien aufteilen und in einem Package zusammenfassen - Eigene Pakete, z.B. für Listen: ListPackage.List Objektorientierung: - Vererbung (inheritance): Verallgemeinerung <-> Spezialisierung (Z.b. Rennfahrer ist allgemein Sportler ist allgemein Mensch oder Mensch kann speziell Sportler sein…) - Konzeptbaum (S. 111) der Klassenhierarachie. (class Fahrzeug {…} class Auto extends Fahrzeug {…} class PW extends Auto {…} … Fahrzeug f; Auto a; erlaubt: f=a; verboten: a=f;) Überprüfung: if (f instanceof Auto) … - Polymorphismus: Objekt kann verschiedene Formen annehmen. abstract class geo_obj{ geo_obj next; public abstract double flaechenwert();} Abstrakte Methoden müssen in abstrakten Klassen sein, abstrakte Methode wird je nach Objekt verschieden definitert (PI * r2 für Kreis, a*b für Rechteck, …) - Mehrfachvererbung (von verschiedenen übergeordneten Klassen) in Java nicht möglich. - Inteface deklariert Methoden, implementiert sie aber nicht. interface interf { boolean kleiner(); boolean equal()} Muss von anderer Klasse implementiert werden: class S implements iterf {public boolean kleiner() {…} …} - Interface kann mehrere andere Intefaces erweitern: interface I extends J, K {…}, Klassen können nur eine Klasse erweitern, aber mehrere Interfaces implementieren: class S extends P implements J, K Exceptions: - Ausgelöst vom System oder explizit im Programm (throw), können abgefangen werden (catch). - try {bla1; bla2;} catch (bla1Exception) {…} catch (bla2Exception) {…} - Typische: IOException, Netz (z.B. MalformedURLException), Objekte erzeugen mit new, Typenkonvertierung (z.B. NumberFormatException) - Abfangen mit try – catch in der Methode oder mit public methode1() throws java.io.IOException {…} wird der Fehler an die aufrufende Methode weitergereicht. (Achtung: import java.io.* nicht vergessen) - Beispiel: try{ out = new FileOutputStream(args[0]);} catch (Throwable e) { System.out.println(“Error!”); System.exit(1);} throwable ist oberste Fehlerklasse und fängt alles ab. Zeigergeflechte: - zyklische Liste (z.B. für das Josephus-Problem) - Binärbäume als „Liste“, bei welcher jedes Element auf zwei weitere zeigt (left und right) und ev. auch auf das Vorgänger-Element - Binäre Suchbäume (left kleiner, right görsser): Einfügen in Baum = durchlaufen bis zu bei Blatt angelangt (Æ links oder rechts anfügen) Löschen im Suchbaum: Bei Blättern und Knoten mit nur 1 Nachfolger einfach. Bei Knoten mit 2 Nachfolgern: Ersetzen durch grösstes Element im linken oder durch kleinstes Element im rechten Teilbaum Inorder-Traversierung Æ Ausgabe ist aufsteigend sortiert. - Sortieren mit Suchbäumen (mit n Knoten): Blatt nach ca. log(n) Schritten, InorderTraversierung braucht c*n Schritte Æ Zu sortierende Liste in Suchbaum überführen und dann Inorder-Traversieren: c*n*log(n) Binärsuche: - Suche in Sortiertem Array: Liegt wert links oder rechts der Mitte? Æ Iterativ oder Rekursiv auf entsprechende Hälfte anwenden. Achtung: Abbruchbedingung: while(re >=li)…(nicht !=) sonst könnte es eine Endlosschlaufe geben. - Optimierung: Nicht einfach in der Mitte zu Suchen beginnen, sondern Abhängig von der Grösse der gesuchten Zahl im Vergleich zur grössten Zahl (bzw. im Vergleich zur Länge des Arrays.) Backtracking: - Systematisches Durchlaufen aller Möglichkeiten. (Baum) - Beispiel: Das Spiel mit den 9 Quadraten, die nur auf eine Art zusammenpassen oder das Schachbrett mit 8 Damen, die sich nicht bedrohen sollen. - Backtracking: Im Baum ersten Ast ganz links nehmen. Dann das Teilproblem für den Unterbaum lösen (unter den neuen Bedingungen). - Strategie: Sackgassen möglichst früh entdecken Æ spart viel Aufwand (ganze Unterbäume) Spielbäume: - Grundannahme: Jeder Spieler verhält sich rational und versucht möglichst grossen Gewinn erreichen. - Spiele endlich, rein strategisch, nur 2 Personen. (Koalitionen etc. bei n Spieler) Nullsummenspiel, vollständige Information. - - MiniMax-Algorhythmus (Negamax) Bei binärem gewinn: sobald man eine sichere 1 hat, können die restlichen Äste geschnitten werden. Suchstrategien: depht-first, breadth-first, best first Alpha-Beta-Algorithmus (Skript S. 180) Nullfenster … (!) Rekursives Problemlösen: - divide et impera - z.B. Türme von Hanoi: Nur eine mögliche Situation am Ende Æ Teilproblemlösen „rückwärts“ - Mergesort: unsortierte Liste wird mit „divide et impera“ zerlegt. Danach werden die Listen wider zusammengeführt und immer gleich sortiert. Komplexität von Algorithmen: - O(f(n)), es wird vereinfacht: c*nÆn, c+nÆn, n+n2Æn2 (n eingabemenge) - Beispiele: ägyptische Multiplikation (m,n) O(log(n)); hanoi O(2n); insertion/ deletion sort O(n2); Sortieren mit Suchbäumen O(n*log(n)) (avarage) bis O(n2) (worst); Mergesort O(n*log(n)); Binärsuche O(log(n)) - wir verwenden Ω ( f ( n ) ) (wächst mindestens so schnell, O: höchstens) - Übungen!!!! Simulation: - Simulation: Experimente an einem Simulationsmodell. (für komplexe Probleme). Im Allgemeinen aufwendig. - Modell: vereinfachtes Abbild der komplexen Realität - Anwendungen: Optimierung, Entscheidungshilfe, Prognose, Validierung, Animation, etc. - Zeitgesteuerte (synchrone) Simulation: Pro Simulationsschritt Erhöhung um festes t (gute Wahl!) Bsp: Katzen, Mäuse, Weizen - spezielles: Schritte berechnen, Bedingungen für spezielle Situationen (Menge = 0 oder = sehr gross); z.T. Random verwenden! Ausgabe ev. periodisch. - Ereignisgesteuerte Simulation: Zustand bleibt zwischen zwei Ereignissen konstant, Zeit springt von Ereignis zu Ereignis. Wichtig: Menge vorgemerkter Ereignis darf nie leer werden Æ bearbeiten eines Ereignisses erzeugt immer mindestens ein neues Ereignis in der Menge. z.B. Callcenter - guter Trick: eintreffendes Ereignis (eingehender Anruf) würfelt Zeitpunkt des nächsten selben Ereignisses aus (nächster Anruf) Æ es müssen nicht alle Ereignisse von Anfang an eingeplant werden. - Programmierung: z.B. Abstrakte Datentypen für Ereignisse, sortiert (priority queues). insert und get_min nötige Funktionen. - Heap-Datenstruktur: Binärbaum mit alle Niveaus gefüllt (von links nach rechts) und Unterbäume enthalten immer grösseren Wert als ihre Wurzel. get_min: 1. Wurzel entfernen, 2. letzen Knoten (unten rechts) nach oben nehmen. 3. diesen so weit wie möglich nach unten wandern lassen (mit kleinerem Nachfolger vertauschen) insert: 1. ganz unten links anfügen, 2. nach oben wandern lassen, solange Vorgänger grösser ist. O(log(n)) Schritte Heap als Array: Wurzel hat Index 1 (0 bleibt leer), Nachfolger von i sind 2i und 2i+1 - Sortieren mit Heap: O(n*log(n)) und kein zusätzlicher Speicherbedarf! Parallele Prozesse und Threads - Prozess = Instanz eines laufenden Programms - Zustand: laufend oder blockiert (siehe S. 263) - PCB: Prozesskontrollblock - Threads („Leichtgewichtsprozesse“): Kontextwechsel viel effizienter als bei Prozessen - Java-Befehle für Threads: S. 268 - class myThread extends Thread { int mynumber; public myThread (int number) {mynumber = number;} public void run() { … } } - Erzeugen aus anderem Thread: myThread m = new myThread(5); m.start(); - alternative: new myThread(5).start(); kann aber nicht mehr kontrolliert werden. - …………(S. 269) Listen-Klasse und Operationen!! (siehe Skript und Übungen)