4. Algorithmen
Motivation Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems Der Begriff Algorithmus geht auf den Gelehrten Muhammad al-chwarizmi zurück, der um das Jahr 800 wirkte Wir haben bereits einige Algorithmen kennengelernt z.b.: Umwandlung in b-adische Darstellung Berechnen von Zweierpotenzen Quelle: en.wikipedia.org 2
Motivation Zwar könnten wir alle Algorithmen als RAM-Programme beschreiben, diese würde jedoch sehr umfangreich wie das folgende Beispiel illustriert Beispiel: Bestimmen des Maximums von drei Zahlen x, y und z in s[0], s[1] und s[2]. Das Ergebnis soll in der Speicherstelle s[3] abgelegt werden. 3
Motivation INPUT 0..2 OUTPUT 3 0: a <- s[0] 1: a <- a - s[1] 2: if a > 0 then jump 10 // s[0] > s[1]? 3: a <- s[1] 4: a <- a - s[2] 5: if a > 0 then jump 8 // s[1] > s[2]? 6: a <- s[2] 7: jump 15 8: a <- s[1] 9: jump 15 10: a <- s[0] 4
Motivation 11: a <- a - s[2] 12: if a > 0 then jump 14 // s[0] > s[2]? 13: jump 6 14: a <- s[0] 15: s[3] <- a 16: HALT Selbst einfache Probleme können zu langen und schwer verständlichen RAM-Programmen führen Wir beschreiben Algorithmen daher im Folgenden mittels einer höheren Programmiersprache 5
Inhalt 4.1 Code 4.2 Suchen 4.3 Sortieren 6
4.1 Code Algorithmen lassen sich mittels sogenannten Pseudocode beschreiben, der an höhere Programmiersprachen (z.b. C/C++, Java) angelehnt ist Es gibt verschiedene Varianten von Pseudocode, die jedoch in den verwendeten Konzepten übereinstimmen Wir verwenden einen Pseudocode, der an Java angelehnt ist, aber nur eine Teilmenge der Sprachbestandteile verwendet und z.b. auf Objektorientierung verzichtet 7
4.1.1 Elementare Datentypen RAM-Programme waren auf ganze Zahlen beschränkt Unser Pseudocode kennt die elementaren Datentypen boolean für Boolesche Werte (d.h. true und false) int für ganze Zahlen (z.b. 41) float für Kommazahlen (z.b. 3.14) char für Zeichen (z.b. a und z ) Diese vier elementaren Datentypen genügen uns, da wir bei der Beschreibung von Algorithmen auf Details (z.b. 32 Bit oder 64 Bit) verzichten 8
4.1.2 Variablen Bei RAM-Programmen mussten wir uns merken, wo welche Information im Speicher abgelegt ist Unser Pseudocode kennt benannte typisierte Variablen, deren Namen sich aus Klein- und Großbuchstaben sowie dem Unterstrich (_) zusammensetzen Variablen werden durch Angabe ihres Datentyps und ihres Namens im Code deklariert z.b. 1 int n; 9
<latexit sha1_base64="8oej4gwvxe055swackhvuxkiup8=">aaacv3icdzbla1nbfmdprlrj9zwqozcxs6ir+0glporcwi0boultfpplodm9scfmi5ljabjki/lnunor+b10thqri4dz/b8zb+bhrbq+lovtk7l3/8how/aj9pgtp8+ed3zfnhqzcjxg3ejjzhl6kkltkigg6dw6qsuknbh5+9/92tu5l4w+cutltckzflpbmcro0jkzm5oj3xzga1ylzbhjqdowhwvtlj4gqdah09ee9abpvbommytup68obim/qpfxapdwb5cosv9u9kw63tlvhfbl/cpsrqjycj3d4styz/gk82n8afhckq5covcxvwld3aalgktapeofj4t8jjo6wnkijvc3mzkkgltu11fqvotrzg1nq2xqeb9u7e6omfxth5hepcdz/ywp037dcg0xgtsp3vo2xchhxmwckythfb+xxctgip7jfxid3rqkutm+j3kvrjx/gwt/f6e9vir6u6877g84qrtewxt4cxw8gyf8ggmyaycv8bw+wffwbetnspo0n1et1p83l2frkt1fgag0xg==</latexit> <latexit sha1_base64="8oej4gwvxe055swackhvuxkiup8=">aaacv3icdzbla1nbfmdprlrj9zwqozcxs6ir+0glporcwi0boultfpplodm9scfmi5ljabjki/lnunor+b10thqri4dz/b8zb+bhrbq+lovtk7l3/8how/aj9pgtp8+ed3zfnhqzcjxg3ejjzhl6kkltkigg6dw6qsuknbh5+9/92tu5l4w+cutltckzflpbmcro0jkzm5oj3xzga1ylzbhjqdowhwvtlj4gqdah09ee9abpvbommytup68obim/qpfxapdwb5cosv9u9kw63tlvhfbl/cpsrqjycj3d4styz/gk82n8afhckq5covcxvwld3aalgktapeofj4t8jjo6wnkijvc3mzkkgltu11fqvotrzg1nq2xqeb9u7e6omfxth5hepcdz/ywp037dcg0xgtsp3vo2xchhxmwckythfb+xxctgip7jfxid3rqkutm+j3kvrjx/gwt/f6e9vir6u6877g84qrtewxt4cxw8gyf8ggmyaycv8bw+wffwbetnspo0n1et1p83l2frkt1fgag0xg==</latexit> <latexit sha1_base64="8oej4gwvxe055swackhvuxkiup8=">aaacv3icdzbla1nbfmdprlrj9zwqozcxs6ir+0glporcwi0boultfpplodm9scfmi5ljabjki/lnunor+b10thqri4dz/b8zb+bhrbq+lovtk7l3/8how/aj9pgtp8+ed3zfnhqzcjxg3ejjzhl6kkltkigg6dw6qsuknbh5+9/92tu5l4w+cutltckzflpbmcro0jkzm5oj3xzga1ylzbhjqdowhwvtlj4gqdah09ee9abpvbommytup68obim/qpfxapdwb5cosv9u9kw63tlvhfbl/cpsrqjycj3d4styz/gk82n8afhckq5covcxvwld3aalgktapeofj4t8jjo6wnkijvc3mzkkgltu11fqvotrzg1nq2xqeb9u7e6omfxth5hepcdz/ywp037dcg0xgtsp3vo2xchhxmwckythfb+xxctgip7jfxid3rqkutm+j3kvrjx/gwt/f6e9vir6u6877g84qrtewxt4cxw8gyf8ggmyaycv8bw+wffwbetnspo0n1et1p83l2frkt1fgag0xg==</latexit> <latexit sha1_base64="zbxrdjqgoih4fdwwev2umk60kv8=">aaacv3icdzbls1tbfmcn10fjtdzol24uzmfx95eirkjbcnnnwyjrwvzcmfektjmvzibscmkx6zdx51bpd2inms6iejjf/zfzyh7uco58nt/xopxvtfup9y148+pwp+3gzu6f0xplsme00pakgkpbffy89wkvjewqvoalhz/+7y/v0dqu1bmfgiwljbqfcgy+ripgez/iikvqj9zblkzacwsv0orrmgthsbtz5rmv7ggrgw+fbp/y4nppcdin2s3yhav7aafdui/q5nnponhm09zxj28fj29fkebzazlfna0af/o3mk0kks8eohdd5maxfvjpmcbz3j84nmdgmmjrya0ytgu1qi3r2+lyhaqcia6s5ncwygqkc1nj34qs/o1ygeimpvd3arkfdsqkkzpxqfjo5rlkei4gzsyubtg8+z7nbiye4u8ue+dxvyabwe3sigdxwppcihlfxltsiugfrezjzwgqtvbipvlccnjetsg3ckz6hjhf5ie8kqfafe1vtb7vn69gtcwbz2rpop1/bv20cg==</latexit> Wertzuweisung Bei der Deklaration kann einer Variable ein Wert mittels = zugewiesen werden Ebenso kann der Wert einer bereits deklarierten Variablen mittels = geändert werden 1 b = true; 2 n = 21; 3 r = 2.78; 4 c = z ; 10
4.1.3 Arrays Arrays (Felder) speichern mehrere Werte des gleichen elementaren Datentyps (in einem zusammenhängenden Bereich des Speichers) Bei der Deklaration wird die Länge des Arrays angegeben 1 int[] a = new int[10]; Die Länge eines Arrays können wir wie folgt ermitteln 1 int n = a.length; und z.b. in einer anderen Variable n ablegen 11
Arrays Den Werten eines Arrays der Länge n sind die Indizes 0 bis (n-1) zugewiesen und wir können auf einen einzelnen Wert wie folgt zugreifen 1 int first = a[0]; 2 int second = a[1]; 3 int last = a[a.length - 1]; 12
4.1.4 Operationen Auf Variablen unserer elementaren Datentypen stehen uns die folgenden Operationen zur Verfügung Addition (+), Subtraktion (-), Multiplikation (*) und Division (/) auf int und real Ganzzahliger Rest (%), Inkrement (++) und Dekrement (--) auf int Logisches Und (&&), Oder ( ) und Nicht (!) auf boolean Es gelten die bekannten Vorrangregeln und es dürfen Klammern gesetzt werden 13
4.1.5 Verzweigungen RAM-Programme kannten (bedingte) Sprungbefehle, wir mussten das Ziel des Sprungs genau festlegen und es waren nur Vergleiche gegen Null möglich Unser Pseudocode kennt eine komfortablere Verzweigung 1 if (Bedingung) { 2 A 3 } else { 4 B 5 } Gilt die Bedingung werden die Befehle in A ausgeführt, andernfalls die Befehle in B 14
Verzweigungen Die Bedingung wird als logischer Ausdruck angegeben und darf die folgenden Operatoren enthalten Vergleiche (==,!=, >=, <=, < und >) Boolesche Operatoren (&&, und!) Klammern Die geschweiften Klammern ({}) markieren einen Gültigkeitsbereich (scope) und enthalten eine Folge von Befehlen Variablen, die innerhalb eines Gültigkeitsbereichs deklariert werden, sind nur darin sichtbar 15
Verzweigungen Verzweigungen können geschachtelt werden, zudem kann der else-teil einer Verzweigung entfallen Den Rückgabewert eines Programms legen wir in unserem Pseudocode mittels return fest 16
Verzweigungen Beispiel: Nehmen wir an, dass bereits drei ganzzahlige Variablen x, y und z deklariert sind, so können wir deren Maximum (vgl. letztes RAM-Programm) bestimmen als 17
Verzweigungen 18
4.1.6 Schleifen RAM-Programme mussten Schleifen mit Hilfe von Sprungbefehlen realisieren Unser Pseudocode kennt zwei Arten von Schleifen while-schleife 1 while (Bedingung) { 2 A 3 } führt die Befehle in A aus, solange die Bedingung gilt 19
Schleifen Beispiel: Summieren der Zahlen von 1 bis 100 1 int n = 100; 2 int sum = 0; 3 while (n > 0) { 4 sum = sum + n; 5 n--; 6 } 7 return sum; 20
Schleifen for-schleife 1 for(initialisierung; Bedingung; Änderung) { 2 A 3 } führt die Befehle in A aus, solange die Bedingung gilt. Die Initialisierung wird vor dem ersten Schleifendurchlauf und die Änderung nach jedem Durchlauf ausgeführt 21
Schleifen Beispiel: Summieren der Zahlen von 1 bis 100 1 int n = 100; 2 int sum = 0; 3 for( int i=1; i <= n; i++) { 4 sum = sum + i; 5 } 22
4.1.7 Funktionen Funktionen (Methoden in Java) erlauben uns, häufig verwendete Folgen von Befehlen zu kapseln und werden durch Angabe einer Signatur wie folgt deklariert Datentyp des Rückgabewerts (void, falls es keinen solchen gibt) Name der Funktion (Buchstaben und Unterstrich) Argumente mit Namen und Datentyp Den Rückgabewert einer Funktion legen wir in unserem Pseudocode mittels return fest 23
Funktionen Beispiel: Summieren der Zahlen von 1 bis n als Funktion 1 int sum_one_to_n(int n) { 2 int sum = 0; 3 for( int i=1; i <= n; i++) { 4 sum = sum + i; 5 } 6 return sum; 7 } 24
4.1.9 Kommentare Zur Erklärung von Programmen kennt unser Pseudocode zwei Arten von Kommentaren Einzeilige Kommentare werden durch // angezeigt 1 // Einzeiliger Kommentar zur Erklä rung Mehrzeilige Kommentare beginnen mit /* und enden mit */ 1 /* 2 * Mehrzeiliger Kommentar zur Erklärung 3 */ 25
Berechnung von Zweierpotenzen Beispiel: Wir implementieren nun die einfache Berechnung von Zweierpotenzen in Pseudocode; als Argument soll eine Variable int n zur Verfügung stehen 26
Berechnung von Zweierpotenzen 27
4.1.8 Mächtigkeit von RAM und Pseudocode Wir hatten gesagt, dass man mit der RAM alles, was berechenbar ist, auch berechnen kann Pseudocode und RAM sind gleich mächtig, d.h. man kann mit beiden die gleichen Probleme lösen Um diese Aussage zu beweisen, müssten wir zeigen, dass wir (i) die RAM in Pseudocode und (ii) unseren Pseudocode auf der RAM simulieren können 28
4.2 Suche Häufig muss man feststellen, ob ein bestimmter Wert k in einer gegebenen Menge von Werten enthalten ist (z.b. Nachschlagen einer Telefonnummer) Dieses Problem nennt man Suche und wir lernen nun zwei Algorithmen zu dessen Lösung kennen Wir nehmen an, dass die gegebene Menge von Werten aus ganzen Zahlen besteht; die Algorithmen sind jedoch auch für andere Datentypen anwendbar, sofern eine Ordnungsrelation definiert ist 29
Ordnungsrelation Definition: Eine Ordnungsrelation auf einer Menge X muss folgende Eigenschaften haben reflexiv, d.h. x œ X : x x transitiv, d.h. x, y, z œ X :(x y) (y z) (x z) antisymmetrisch, d.h. x, y œ X :(x y) (y x) x = y 30
Ordnungsrelation Für ganzen Zahlen und Kommazahlen dient uns als Ordnungsrelation Für Zeichen betrachten wir den zugehörigen Code (z.b. ASCII) und verwenden wiederum als Ordnungsrelation 31
4.2.1 Lineare Suche Die gegebene Menge von Werten soll uns als Array ganzer Zahlen a bereit stehen und wir möchten einen Index bestimmen, an dem der gesuchte Wert k steht Ist der gesuchte Wert k nicht im Array enthalten, soll -1 zurückgegeben werden Wir schreiben eine Funktion mit folgender Signatur 1 int linearsearch(int[] a, int k) { 2... 3 } 32
Lineare Suche Idee: Durchlaufe das Array vom Anfang zum Ende und gib den ersten Index zurück, an dem k vorkommt; wird das Ende erreicht, gib -1 zurück 1 int linearsearch(int[] a, int k) { 2 for( int i = 0; i < a.length; i++) { 3 if (a[i] == k) { // k gefunden 4 return i; 5 } 6 } 7 return -1; // Ende erreicht 8 } 33
Lineare Suche Beispiel: Betrachte folgendes Array ganzer Zahlen a 3 1 7 2 5 1 9 Suche nach k = 7 3 1 7 2 5 1 9 a 2 Suche nach k = 8 3 1 7 2 5 1 9 a -1 34
Laufzeit der lineare Suche Wir messen die Laufzeit eines Programms in Pseudocode als die Anzahl der abgearbeiteten einfachen Befehle (Wertzuweisungen, Operationen, Vergleiche, Rückgabe) Die Laufzeit der linearen Suche hängt offensichtlich von der Anzahl n der Werte im Array a sowie dem konkreten Wert von k ab Im schlechtesten Fall (worst case) wird das Array vollständig durchlaufen und jeder Wert darin mit dem gesuchten Wert verglichen 35
Laufzeit der lineare Suche Zur Initialisierung der Schleife wird ein Befehl ausgeführt Für jeden im Array betrachten Wert werden ausgeführt die Überprüfung der Schleifenbedingung (Zeile 2) der Vergleich des Werts mit dem gesuchten Wert (Zeile 3) das Inkrement der Schleifenvariable (Zeile 2) Zudem wird genau ein Wert zurückgegeben Bei Abbruch der Schleife die Überprüfung (Zeile 2) Im schlechtesten Fall werden 3n + 3 Befehle abgearbeitet Die Zeitkomplexität der linearen Suche liegt damit in O(n) 36
Zusammenfassung Algorithmen als systematische Vorgehensweise zur Lösung eines formal definierten Problems Pseudocode gibt uns eine Möglichkeit, Algorithmen kompakter und verständlicher zu beschreiben, als dies mit RAM-Programmen möglich ist Lineare Suche findet einen bestimmten Wert in einer gegebenen Menge von Werten und hat lineare Laufzeit 37
Literatur [1] H.-P. Gumm und M. Sommer: Einführung in die Informatik, Oldenbourg Verlag, 2012 (Kapitel 4.1) [2] T. H. Cormen, C. E. Leiserson, R. Rivest und C. Stein: Algorithmen Eine Einführung, Oldenbourg Verlag, 2009 (Kapitel 2) 38