Berufsakademie Stuttgart / Außenstelle Horb Studienbereich Technik Studiengang Informationstechnik Kurs IT2006, 2.Semester Dozent: Olaf Herden Student: Alexander Carls Matrikelnummer: 166270 Aufgabe: Beschreibung des euklidischen Algorithmus Datum: 30.09.2007 Seite 1
Inhaltsverzeichnis Vorwort Seite 2 Die Idee und seine Geschichte Seite 2 Erklärung des Algorithmus Seite 2-4 Varianten des euklidischen Algorithmus Seite 4 Der euklidische Algorithmus als Java-Code Seite 5 Korrektheit & Laufzeitkomplexitätsanalyse Seite 6 Anwendungsgebiete Seite 7 Die eigene Implementierung Seite 7 Quellenverzeichnis Seite 8 Vorwort Diese Dokumentation über den euklidischen Algorithmus wurde im Rahmen der Vorlesung Programmieren an der Berufsakademie Stuttgart / Außenstelle Horb erstellt. Über die Aufgabe hinaus, den Algorithmus zu erklären und Beispiele hierfür zu finden werde ich zusätzlich kurz auf meine eigene Implementierung, die Korrektheit sowie die theoretische Laufzeit eingehen. Ein Dank geht an Herrn Herden sowie an Björn Strobel für die Bereitstellung der Pakete zur Visualisierung und schrittweisen Dokumentation des Algorithmus. Die Idee und seine Geschichte Der euklidische Algorithmus ist ein effizienter Algorithmus zur Berechnung des größten gemeinsamen Teilers (im folgenden ggt abgekürzt) zweier ganzer positiver Zahlen. Der ggt ist die größte Zahl die zwei voneinander verschiedenen Zahlen gemeinsam haben. Beispiel: ggt von 100 und 40 ist 20. Ist die Primfaktorzerlegung zweier Zahlen nicht bekannt, so ist der euklidische Algorithmus der schnellste bekannte Algorithmus zur Berechnung des ggt, denn Algorithmen zur Primfaktorzerlegung erreichen bisher nicht die Laufzeitkomplexität des euklidischen Algorithmus. Erstmals formuliert wurde der Algorithmus 300 v. Chr. von Euklid von Alexandria in seinem Werk Die Elemente. Man geht aber davon aus, dass der Algorithmus bereits vor 300 v. Chr. dem griechischen Mathematiker Eudoxos von Knidos sowie dem Philosophen Aristoteles bekannt war. Auf der Suche nach der gemeinsamen größten Länge zweier Stöcke formulierte Euklid den Algorithmus in Die Elemente folgendermaßen: Wenn CD aber AB nicht misst, und man nimmt bei AB, CD abwechselnd immer das kleinere vom größeren weg, dann muss (schließlich) eine Zahl übrig bleiben, die die vorangehende misst. Erklärung des Algorithmus Um mit Hilfe von Euklids Formulierung den ggt zweier Zahlen zu berechnen muss zuerst die größere der beiden Zahlen ermittelt werden. Anschließend wird die kleinere der beiden Zahlen von der größeren Zahl abgezogen. Dadurch entstehen zwei neue Zahlen nämlich das Ergebnis der Subtraktion so wie die kleinere der beiden alten Zahlen. Nun wird überprüft ob beide Zahlen gleich groß sind. Ist dies der Fall, so ist der ggt der beiden Zahlen gefunden. Ist dies nicht der Fall wird der selbe Ablauf noch einmal mit den beiden neuen Zahlen durchlaufen, solange bis beide Zahlen gleich groß sind. Seite 2
Zur Verdeutlichung die Erklärung noch einmal an einem Beispiel: 1. Zahl = 4 2. Zahl = 6 Welche der beiden Zahlen ist größer? 2. Zahl = 6 Ziehe die kleinere von der größeren Zahl ab! 2. Zahl = 6 1. Zahl = 4 Wie lautet das Ergebnis? 3. Zahl = 2 Ersetze größere der beiden Zahlen durch 3. Zahl! 1. Zahl = 4 2. Zahl = 2 Sind beide Zahlen gleich? 4 ist ungleich 2 Welche der beiden Zahlen ist größer? 2. Zahl = 4 1. Zahl = 2 Ziehe die kleinere von der größeren Zahl ab! 2. Zahl = 4 1. Zahl = 2 Wie lautet das Ergebnis? 3. Zahl = 2 Seite 3
Ersetze größere der beiden Zahlen durch 3. Zahl! 1. Zahl = 2 2. Zahl = 2 Sind beide Zahlen gleich? 2 ist gleich 2 ggt gefunden Varianten und Erweiterungen des euklidischen Algorithmus 1. Moderner euklidischer Algorithmus Betrachtet man den euklidischen Algorithmus genauer fällt auf, dass es unter Umständen sehr lange dauern kann bis der ggt zweier Zahlen gefunden ist. Dies ist vor allem dann der Fall, wenn eine der beiden Zahlen sehr groß im Vergleich zu anderen ist, wie zum Beispiel bei 2 und 1000001. Mit dem modernen euklidischen Algorithmus ist es möglich dieses Problem zu umgehen. Der moderne euklidische Algorithmus verwendet statt einer Subtraktion die modulo-funktion zur Berechnung des ggt. Die Ermittlung des ggt der Zahlen 101 und 2 würde mit dem modernen euklidischen Algorithmus etwa so aussehen. Schritt 1: 101 ist größer als 2 Schritt 2: Rechne 101 modulo 2 = 1 Schritt 3: Ersetze 101 durch 1 Schritt 4: 2 ist größer als 1 Schritt 5: Rechne 2 modulo 1 = 0 1 ist ggt von 101 und 2 Während man mit dem klassischen euklidischen Algorithmus 50 Rechenschritte benötigt hätte um den ggt zu ermitteln ist es mit dem modernen Algorithmus in nur 2 Rechenschritten möglich. Dies bedeutet eine enorme Zeitersparnis. Doch es ist zu beachten, dass auch der moderne euklidische Algorithmus nicht immer schneller ist. Sind die beiden Zahlen deren ggt ermittelt werden soll zwei aufeinander folgende Fibonacci Zahlen, so stößt der moderne Algorithmus auf seine Grenzen und benötigt genauso viele Schritte wie der klassische Algorithmus. Im Regelfall jedoch halbiert sich die größere der beiden Zahlen immer mindestens um die Hälfte und macht den modernen Algorithmus sehr schnell. 2. Erweiterter euklidischer Algorithmus Der so genannte erweiterte euklidische Algorithmus oder auch binärer euklidischer Algorithmus von Josef-Stein ist in der Lage, mittels Division durch 2, nebst dem ggt zweier Zahlen auch die Inverse und den Restklassenring zu berechnen. Das so genannte additive Inverse von 8 ist z.b. -8. Das multiplikativ Inverse von 8 ist 1/8. Unter dem Restklassenring versteht man die Menge von Zahlen die entstehen können wenn man eine positive ganze Zahl modulo einer anderen Zahl n berechnet. So ist der Restklassenring einer Zahl modulo 2 z.b. entweder 0 oder 1. Diese Restklassenringe spielen vor allem in der Mikroprozessortechnik eine große Rolle. So sind Integer Zahlen mit 16 bit im Mikroprozessor als folgender Restklassenring dargestellt: Z/ 65536Z (Z = ganze Zahlen). Seite 4
Der euklidische Algorithmus als Java-Code 1. Klassischer euklidischer Algorithmus - Iterativ implementiert KlassischEuklidIterativ(a,b) while(b!=0) { // Prüft ob kleinere Zahl bereits 0 ist if (a > b) { a = a-b; /* Wenn keine der beiden Zahlen 0 ist, wird die kleinere Zahl von der größeren abgezogen */ else { b = b-a; return a; 2. Klassischer euklidischer Algorithmus Rekursiv Implementiert KlassischEuklidRekursiv(a,b) if (b==0) { // Prüft ob kleinere Zahl bereits 0 ist return a; else if (a > b) { /* Größere Zahl wird ermittelt, kleinere von return KlassischEuklidRekursiv(a-b, b); größerer Zahl abgezogen und die Funktion mit den beiden neuen Zahl else { erneut aufgerufen */ return KlassischEuklidRekursiv(a, b-a); 3. Moderner euklidischer Algorithmus Iterativ implementiert ModernEuklidIterativ(a,b) int h; // Hilfsvariable while (b!= 0) { // Prüft ob b bereits 0 ist h = a % b; /* Hilfsvariable mit a modulo b setzen, 1. Zahl a = b; durch 2. Zahl ersetzen, 2. Zahl durch h ersetzen b = h; */ return a; 4. Moderner euklidischer Algorithmus Rekursiv implementiert ModernEuklidRekursiv(a,b) if (b = 0) { return a; else { return ModernEuklidRekursiv(b, a mod b) // Prüft ob b schon 0 ist /* Erneuter Aufruf der Funktion mit Zahl 2 als Zahl 1 und a mod b als Zahl 2 */ Seite 5
Korrektheit & Laufzeitkomplexität Korrektheit: Annahme: 1. Zahl und 2. Zahl sind > 0 Invariante: 1.Zahl <= 0 ^ 2. Zahl <= 0 Vor Beginn der Schleife: Wenn sich der Benutzer an die Vorgaben hält so ist die Invariante erfüllt Während der Schleife: Nach der Schleife: Da immer die kleinere von der größeren Zahl abgezogen wird, kann keine der beiden Zahlen kleiner als 0 werden Da die Zahlen während der Schleife nicht kleiner 0 werden können und nach der Schleife keine weitere Anweisung folgt, können die Zahlen auch danach nicht kleiner werden. Somit gilt: Der Algorithmus terminiert und ist korrekt. Dies gilt analog für den modernen euklidischen Algorithmus, da bei einer modulo-funktion nie ein negativer Rest entstehen kann, in sofern die beiden Zahlen zu Beginn positive ganze Zahlen sind. Laufzeit: Geht man davon aus, dass der klassische euklidische Algorithmus erkennt, dass wenn eine der beiden Zahlen = 1 ist, der ggt ebenfalls 1 ist, ergibt sich folgende Laufzeitkomplexität. Eingabe: n und m, dabei gilt n ist größer als m Schritt 1: n m Schritt 2: n m Schritt 3: n m usw. usw. Daraus ergibt sich als Laufzeitkomplexität: n / m Oder in der Landau-Schreibweise: Θ n In Worten ausgedrückt bedeutet dies, dass der euklidische Algorithmus im schlimmsten Falle halb so viele Subtraktionen durchführen muss, wie die größere Zahl zu Beginn groß ist. Worst Case Average Case Best Case Θ n (n/2) Θ n (n/2) Θ 1 Beispiel: Zahl 1 = 10 Zahl 2 = 2 Schritt 1: 10-2 = 8 Schritt 2: 8 2 = 6 Schritt 3: 6 2 = 4 Schritt 4: 4 2 = 2 Schritt 5: 2-2 = 0 n/m = 10 / 2 5 Zusatzinfo: Interessanterweise, ist die Laufzeit des modernen euklidischen Algorithmus bei zwei aufeinander folgenden Fibonacci-Zahlen Θ log (a b) Seite 6
Anwendungsgebiete Der euklidische Algorithmus wird unter anderem in der Kryptographie verwendet. Als Beispiel kann hier das so genannte RSA-Verfahren genannt werden, das eine asymmetrische Verschlüsselungsmethode darstellt. Hier wird der euklidische Algorithmus zur Berechnung von private und public Key verwendet und ermöglicht somit die Verschlüsselung so wie die digitale Signatur von Daten. Die eigene Implementierung Wahl des Algorithmus der angewendet werden soll (Klassisch oder Modern) Eingabe der Zahlen beiden Zahlen (nur positive, ganze Zahlen) Darstellung der Berechnung Seite 7
Quellenverzeichnis Algorithmen Eine Einführung 2. Auflage Th. H. Cormen, Ch. E. Leserson, R.Rivest, C.Stein Oldenbourg Verlag München Wien Seiten 858 864 http://de.wikipedia.org/wiki/euklidischer_algorithmus http://de.wikipedia.org/wiki/erweiterter_euklidischer_algorithmus http://de.wikipedia.org/wiki/steinscher_algorithmus http://de.wikipedia.org/wiki/rsa Die Elemente Euklid Wissenschaftliche Buchgesellschaft Darmstadt http://www-i1.informatik.rwth-aachen.de/~algorithmus/algo18.php Seite 8