Algorithmen und Datenstrukturen 08
Stefan Ploner
15. Dezember 2011
1 Besprechung Blatt 7 Polymorphie Fragen
2 Dynamische Programmierung Allgemein
3 Fortgeschrittene Programmiertechniken Generics und komplexe Datenstrukturen 4 Vorbereitung Blatt 8
Anmerkungen
Polymorphie
Vehicle v = new Boat();
// statischer Typ: Vehicle // dynamischer Typ: Boat
v.move(); // was passiert hier?
Java geht wie folgt vor...
1 existiertmove()in Vehicle? Nein → Fehler
2 ist move()alsstaticdefiniert? Ja → Vehicle.move()
3 existiertmove()in Boat? Nein → Vehicle.move()
4 sonst → Boat.move()
Fragen zu Blatt 7?
Allgemein
• hat nichts mit Dynamik zu tun
• Tausch: Speicher gegen Performance
• bei der ersten Berechnung werden die Ergebnisse gespeichert
• bei folgenden Funktionsaufrufen wird lediglich das gespeicherte Ergebnis zur¨uckgegeben
• bei nicht linearen Algorithmen ist schon im ersten Durchlauf ein Performancegewinn m¨oglich
Umwandlung in einen dynamischen Algorithmus
• Speicherstruktur anlegen (Initialisieren der Klasse / 1. Aufruf)
• Pr¨ufen, ob Wert vorhanden ist (vor der Berechnung)
• Speichern des Ergebnisses (nach der Berechnung, immer wenn diese ausgef¨uhrt werden musste)
Details zur Speicherstruktur
• als Speicherstruktur bei Funktionen mit einer Ganzzahl als Parameter eignen sich Arrays (Parameter→ Speicherzelle)
• diese erm¨oglichen die ¨Uberpr¨ufung, ob der Wert bereits berechnet wurde, und gegebenenfalls die R¨uckgabe in O(1)
• MAX N ¨uberlegen, sonst br¨auchte man bei einem Integer Parameter ¨uber 4 Millionen Arrayelemente
• dieser Wertebereich muss dann nat¨urlich ¨uberpr¨uft werden
• Funktionen mit mehreren Argumenten ben¨otigen entsprechend viele Arraydimensionen (int[MAX_N][MAX_M]...)
• werden alle Werte des Array-Typs ben¨otigt, muss man sich anderswo merken, welche Felder initialisiert sind.
Fibbonacci Zahlen dynamisch
class Fibonacci {
final int MAX_N = 200;
int[] hirn;
public Fibonacci() {
hirn = new int[MAX_N + 1]; // last index: MAX_N Arrays.fill(hirn, -1);
hirn[0] = 0; // "implicit" base cases hirn[1] = 1;
}
public dynamic(int n) { if (n > MAX_N) return -1;
if (hirn[n] == -1) {
hirn[n] = dynamic(n - 1) + dynamic(n - 2);
}
return hirn[n];
} }
Schema eines dynamischen Algorithmus
(Im Konstruktor Speicherstruktur anlegen und evtl. initialisieren)
In der Berechnungsmethode
1 Parameter im Wertebereich? Nein →return;
2 Wert bereits gespeichert? Nein→ ausrechnen
3 gespeicherten Wert zur¨uckgeben
Fibbonacci Zahlen durchreichen
public static int durchreichen(int n) { if (n <= 1) return n;
return durchreichen(2, 1, 0, n);
}
// (viel Stackspeicher, besser auch entrekursivieren) public static int durchreichen(int n, int prevRes,
int prevPrevRes, int max_n) { // fib(n) = fib(n - 1) + fib(n - 2) int curRes = prevRes + prevPrevRes;
if (n == max_n) return curRes;
else {
// prevRes = curRes, prevPrevRes = prevRes return durchreichen(n + 1, curRes, prevRes, max_n);
} }
Listenklassen in der Java API
Problemstellung: Man m¨ochte einer Speicherstruktur dynamisch Daten hinzuf¨ugen und entfernen.
Bisher:Manuelles Erstellen und Kopieren von Arrays Besser:M¨oglichkeiten der Java API nutzen
List v = new ArrayList();
v.add("foo"); // add "foo"
v.add("bar"); // add "bar"
v.set(5, "bla"); // IndexOutOfBoundsException System.out.println(v.get(0)); // print "foo"
v.remove(0); // remove "foo"
Generische Listenklassen
Probleme einerArrayList:
List v = new ArrayList();
v.add("foo");
Integer i = (Integer)v.get(0); // runtime error Der selbe Code mit Generics:
List<String> v = new ArrayList<String>();
v.add("bar");
Integer i = v.get(0); // compiler error Generics dienen der ¨Ubersichtlichkeit, Fehlersuche und Performance. Die Java API bietet komplett implementierte generische Datenstrukturen f¨ur viele Anwendungsf¨alle bereit!
Wrapper-Klassen
• Generic Type Parameter erlauben nur Object und dessen Unterklassen als Argument
• ValueTypes erbennicht von Object(boolean, char, int, ...)
• stattdessen m¨ussen sogenannte “Wrapper-Klassen” verwendet werden(Boolean, Char, Integer, ...)
• dabei wird der ValueType in die Klasse eingepackt (“Boxing”) class Integer {
int value;
// probably useful Methods }
Boxing und Unboxing
Vorsicht: Obwohl es sich um Klassen handelt, weisen Wrapper-Klassen keine typische Referenzsemantik auf Ausf¨uhrliches Rechenbeispiel:
Integer a = new Integer(5), b;
b = a;
a += 5; // a = a + 5;
// a is now 10, b is still 5 Zeile 3:
1 a wird implizit nach int konvertiert (unboxing)
2 die Rechnung 5 + 5 wird ausgef¨uhrt
3 10 wird implizit nach Integer konvertiert, dabei wird eine neue Instanz erstellt (boxing)
Beispiel: Großeinkauf
Stellen Sie sich vor es ist Montag und Sie haben diese Woche besonders viele AuD-Hausaufgaben auf. Um unn¨otige Ablenkungen auf ein Minimum zu beschr¨anken, m¨ochten Sie alle
Lebensmitteleink¨aufe schon jetzt erledigen, damit Sie das Haus f¨ur den Rest der Woche nichtmehr verlassen m¨ussen.
Um dieses Ziel zu erreichen beschließen Sie, all Ihr herumliegendes Geld in Nahrungsmittel zu investieren. Um Ihre Einkaufstour zu optimieren entwickeln Sie vor dem Aufbruch einen Algorithmus, der die Eink¨aufe so ausw¨ahlt, dass so wenig ungenutztes Restgeld wie m¨oglich ¨ubrigbleibt.
Beispiel: Großeinkauf
Rekursionsidee, um diese Einkaufsliste zu erstellen:
besteListe : Einkaufsliste;
fuer jeden bezahlbaren Artikel des Supermarkts { neueListe = Rekursion mit dem restlichen Geld;
neueListe += aktuellerArtikel;
wenn neueListe teurer als besteListe { besteListe = neueListe;
} }
Beispiel: The Berch
Stellen Sie sich vor, es ist Bergkirchweih in Erlangen, und Sie gehen hin, um Ihr ganzes BAf¨oG f¨ur Juni auf den Kopf zu hauen.
Als fleißiger AuD-Teilnehmer betrachten Sie nat¨urlich auch diese Aufgabe als spannendes algorithmisches Problem f¨ur geschickte Datenstrukturen. Noch vor der ersten
”Mass Bier“ stellen Sie sich ein Array aller k¨auflichen Berg-Dinge (vom Typ
BerchWareInterface) zusammen, f¨ur die Sie grunds¨atzlich bereit sind, Ihr Geld auszugeben. Die Festwirte garantieren Ihnen, dass Sie von jedem Nahrungsmittel und jeder Dienstleistung beliebig viel haben d¨urfen, solange Sie daf¨ur noch bezahlen k¨onnen.
Erg¨anzen Sie nun die Methode Berch.getAll so, dass diese alle denkbaren Einkaufslisten zusammenstellt, die Sie mit dem Geldbetrag money (in Cent, genau wie pricePerUnit) aus dem Angebot choice gerade noch kaufen k¨onnen. Dabei ist es
entscheidend, dass Sie – wie es sich f¨ur einen typischen Studenten geh¨ort – so viel wie m¨oglich von Ihrem Geld ausgeben, d.h. jede einzelne Einkaufsliste k¨onnte nicht mal mehr mit der billigsten BerchWare erg¨anzt werden, ohne dabei
”ins Minus“ zu rutschen.
// money: maximal verfuegbares Geld // choice: alle verfuegbaren Berg-Dinge
public static BerchPurchase[][] getAll(int money, BerchWareInterface[] choice) {
Rekursionsidee, um alle bezahlbaren Listenkombinationen zu erstellen:
• kaufe das erste bezahlbare Objekt in choice
• berechne rekursiv alle M¨oglichkeiten, das Restgeld f¨ur die vollst¨andige Auswahl zu verprassen und f¨uge den Kauf zu jeder rekursiv ermittelten Kombination hinzu
• tu so, als wolltest du das erste Objekt nie kaufen, berechne rekursiv alle M¨oglichkeiten, money ohne das erste bezahlbare Element zu verprassen
W¨ahle anschließend alle Einkaufslisten mit Restgeld<billigster choice aus
Der letzte Schritt kann weggelassen werden, wenn die Choice Liste absteigend sortiert wird. Dann werden automatisch nur maximale Einkaufslisten ausgew¨ahlt, da immer mit der letzten und billigsten
Fahrradladen
Unterteilt euren Code bitte halbwegs sinnvoll, legt Hilfsmethoden an (private!) und verseht ihn mit kurzen, pr¨azisen Kommentaren!
Heap- und Stacksize
• -Xmx: maximale Heapgr¨oße
• -Xss: maximale Stackgr¨oße
• als Argumente beim Programmstart mit ¨ubergeben (vor dem Programmnamen!)
• Beispiel: java -Xmx512m -Xss256m Fibonacci
(Das m am Ende steht f¨ur Megabyte, kein Leerzeichen dazw.)
• in Eclipse unter “Run Configuration” →“Arguments” →
“VM arguments” eintragen
Bei der AufgabeZufallszahlengenerator ist es fast garantiert notwendig, den Stack zu vergr¨oßern!
Zufallszahlengenerator
In der Aufgabe geht es um Endrekursivierung, nicht um Entrekursivierung!
0-Punkte-Generator
Erinnerung an Blatt 0: “Schnittstellen¨anderung”
• “Die Schnittstellen von Methoden und Klassen d¨urfen nicht ver¨andert werden.”
• “Auch f¨ur Klassen!”
• D.h., dass s¨amtliche selbst hinzugef¨ugten Attribute und Methoden mit der Sichtbarkeit privateversehen werden m¨ussen, ansonsten gilt auch hier die 0 Punkte Regelung!
• Das impliziert, dass beim Zugriff auf andere Klassen nur die gegebenen Schnittstellen verwendet werden d¨urfen. (Z.B. kein direkter Zugriff auf (ohnehin verbotene) eigene
public-Attribute)