Kapitel II Zahlbereiche Algorithmen bauen auf Elementaroperationen auf, und neue mathematische Begriffe können basierend auf existierenden Begriffen definiert werden. Am Anfang einer derartigen Entwicklung müssen aber gewisse Objekte und Operationen als gegeben angenommen werden. In einem theoretischen Aufbau der Mathematik sind dies die Axiome, die als Basis für den Aufbau einer Theorie zu dienen haben, als Beispiele seien etwa die Zermelo1 -Fraenkel2 Axiome für die Mengenlehre oder die Peano3-Axiome für die natürlichen Zahlen genannt. Von einem theoretischne Standpunkt aus reichen Mengen und die darauf axiomatisch festgelegten Operationen aus, um alle in der Mathematik auftretenden Objekte und Begriffe zu beschreiben. In einem algorithmischen Aufbau der Mathematik empfiehlt es sich jedoch, die auf einem Computer in natürlicher Weise vorhandenen Operationen als Elementaroperationen und die zur Verfügung stehenden Grunddatentypen als Grundobjekte anzunehmen und darauf aufbauend neue Datenstrukturen und neue Algorithmen zu entwickeln. Wir werden nun untersuchen, welche Grundstrukturen auf einem Computer anzutreffen sind, und wie damit verschiedene Arten von Zahlen dargestellt und darauf deren arithmetische Grundoperationen wie Addition, Multiplikation oder Division realisiert werden können. 6 N und Z Schon in den einleitenden Betrachtungen zur Umsetzung von mathematischen Algorithmen auf einem Computer in Abschnitt 1 sind wir der Problematik begegnet, dass unendliche Mengen auf einem Rechner schwer realisierbar sind, und wir in solchen Fällen immer mit gewissen Unzulänglichkeiten und/oder Fehlern rechnen 0 <Id: NZQR.tex 547 2007-12-07 15:18:31Z WW > Ernst: historische Daten 2 Fraenkel, Abraham: historische Daten 3 Peano, Giuseppe: historische Daten 1 Zermelo, 64 Kapitel II. Zahlbereiche müssen. Die natürlichen Zahlen N und darauf aufbauend die ganzen Zahlen Z sind die beinahe einfachsten mathematischen Zahlenmengen, aber auch hier haben wir es bekanntlich mit unendlichen Mengen zu tun. Darstellung am Computer und arithmetische Grundoperationen Wir widmen uns vorerst den natürlichen Zahlen, da sich die ganzen Zahlen daraus (relativ) einfach durch Hinzunahme der negativen natürlichen Zahlen“ ergeben. ” Eine mathematische Definition von N beruht z.B. auf den Peano-Axiomen, mit denen man N als die Menge definiert, die ein spezielles Element 1 enthält4 , und die mit jedem n auch dessen Nachfolger n′ enthält. Addition, Multiplikation und Größenvergleich lassen sich rekursiv basierend auf 1 und der Nachfolgerfunktion ′ definieren. Eine Umsetzung dieser Ideen auf einem Computer ist zwar prinzipiell möglich, aber genauso dem Dilemma des endlichen Speicherplatzes ausgesetzt und darüberhinaus wenig effizient. Zum Rechnen auf Computern bietet sich die Basisdarstellung natürlicher Zahlen an. Zu einer Basis B ≥ 2 kann jede natürliche Zahl z eindeutig als z= ω−1 X zi B i (6.1) i=0 mit ω = ⌊logB (z)⌋ + 1 und 0 ≤ zi < B für alle i geschrieben werden. Fixieren wir B, so ist z durch die Ziffern z0 , . . . , zω−1 (bzgl. B) charakterisiert, und wir nennen zω−1 6= 0 die führende Ziffer. Ziffern werden oft auch Stellen genannt, die Zahl ω beschreibt die Stellenanzahl von z, und für fixe Stellenzahl ω können wir auf diese Art alle Zahlen 0 ≤ z < B ω darstellen. Bevor wir uns mit den arithmetischen Grundoperationen beschäftigen, diskutieren wir, wie natürliche bzw. ganze Zahlen auf einem Computer dargestellt werden. Diese werden – gemeinsam mit den in Abschnitt ?? vorgestellten Gleitkommazahlen – die Grundbausteine zum Aufbau komplizierterer mathematischer Objekte darstellen. Computerrepräsentation (Natürliche und ganze Zahlen fixer Länge). Für die interne Zahlendarstellung in einem Computer wird abhängig von der verwendeten Hardware (dem Prozessor, auch Chip“ genannt) eine Wortlänge ω fixiert und das ” Dualsystem verwendet, d.h. B = 2, um die Ziffern z0 , . . . , zω−1 zu speichern. Heutzutage gebräuchliche Wortlängen sind ω = 16 (32 oder auch 64), womit wir Zahlen zwischen 0 und 216 −1 = 65535 (232 −1 ≈ 4.3 Milliarden bzw. 264 −1 ≈ 1.8·1019 ) – jeweils zur Basis 10 im danach benannten Dezimalsystem – zur Verfügung haben. Die einzelnen Ziffern zi ∈ {0, 1} im Dualsystem werden Bits genannt und in Speicherzellen abgelegt, die nur die Werte 0 und 1 annehmen können, was in elektronischen Schaltelementen durch kein Strom“ und Strom“ repräsentierbar ” ” 4 In manchen Anwendungen erweist es sich als praktisch, auch die Zahl 0 zu den natürlichen Zahlen zu zählen, und wir schreiben N0 für die Menge der natürlichen Zahlen mit 0. 6. N und Z 65 ist. Ein Block von 8 Bits wird auch 1 Byte genannt, in Systemen mit Wortlänge 32 benötigt eine natürliche Zahl somit 4 Bytes Speicher. Sollen auch negative Zahlen darstellbar sein, so verwendet man in der VorzeichenBetrag-Darstellung ein Bit für das Vorzeichen und die restlichen ω − 1 Bits für den Betrag der Zahl. Mit ω = 16 können wir so Zahlen im Dezimalsystem zwischen −215 − 1 = −32767 und 215 − 1 = 32767. In der Vorzeichen-Betrag-Darstellung gibt es zwei Möglichkeiten, die Zahl 0 darzustellen, nämlich −0 und +0. Dies ist in sogenannten B-Komplement-Darstellungen nicht der Fall, wo mathematisch gesprochen jede negative Zahl −z durch B ω − z dargestellt wird. In der Zweierkomplementdarstellung ist dann jede Zahl mit führendem Bit 1 als negative Zahl zu interpretieren. Hier besitzt 0 und auch jede andere Zahl eine eindeutige Darstellung, wodurch (im negativen Bereich) um eine Zahl mehr dargestellt werden kann. Mit 16 Bits können wir damit Dezimalzahlen zwischen −32768 und 32767 darstellen. y Computerprogrammierung (N und Z in gängigen Programmiersprachen). Die meisten Programmiersprachen stellen Datenstrukturen für diese Fragmente von N und Z zur Verfügung. In manchen Sprachen hat man die Wahlmöglichkeit zwischen langen (32 oder 64 Bits) und kurzen Zahlen (16 oder 32 Bits) bzw. zwischen vorzeichenlosen – d.h. natürlichen – Zahlen und solchen mit Vorzeichen – also ganzen Zahlen. In der Praxis findet in gängigen Computerchips meist die Zweierkomplement-Darstellung Anwendung. Im internen Prozessor eines Computers wird also mit Basis 2 gearbeitet, wir wollen in unseren weiteren Betrachtungen und Beispielen allerdings zur gewohnten Basis 10 denken. Alle hier gewonnenen Erkenntnisse und Prinzipien lassen sich leicht auf Darstellungen mit anderer Basis übertragen. Beispiel (Zehnerkomplement-Darstellung). Im Dezimalsystem mit Wortlänge 4 können wir die natürlichen Zahlen von 0 bis 104 − 1 darstellen, also 0, . . . , 9999. In Zehnerkomplement-Darstellung sind unter denselben Voraussetzungen die ganzen Zahlen −5000, . . . , 4999 darstellbar, wobei das Vorzeichen nicht explizit abgespeichert wird. Zwischen 0 und 4999 bleibt alles unverändert, aber jede Zahl mit einer führenden Ziffer 5, 6, 7, 8 oder 9 wird als negative Zahl gedeutet, etwa 5000 als −5000, dann 5001 als −4999, bis hinauf zu 9999, das für die Zahl −1 steht. y Die aus der Schule bekannten Rechenabläufe zum Addieren, Subtrahieren, Multiplizieren und Dividieren sind die klassischen Algorithmen für die Grundrechnenoperationen auf Zahlen in Basisdarstellung. In diesen Verfahren werden einfachste Operationen auf Ziffern als Elementaroperationen vorausgesetzt, nämlich A das Addieren (Subtrahieren) zweier Ziffern resultierend in einer Ziffer und einem Übertrag 0 oder 1, M das Multiplizieren zweier Ziffern resultierend in einer zweistelligen Zahl und D das Dividieren einer zweistelligen Zahl durch eine Ziffer, sofern der Quotient und Rest jeweils Ziffern ergeben. 66 Kapitel II. Zahlbereiche Wenn wir im Zehnersystem mit Papier und Bleistift größere Zahlen addieren, subtrahieren oder multiplizieren, so greifen wir auf einfache Berechnungen A und M als Elemntaroperationen zurück, die wir im Kindesalter auswendig gelernt haben, das Einmaleins“. Auch am Computer kommen die klassischen Algorithmen ” zum Addieren/Subtrahieren/Multiplizieren zweier ω-stelliger Zahlen zum Einsatz, und auch hier basieren sie tatsächlich rein auf elementaren Operationen A und M. Auf niedrigster Stufe sind A und M die Addition bzw. Multiplikation zweier Bits, die in einfachsten Schaltkreisen umgesetzt werden können. Darauf aufbauend lassen sich etwas kompliziertere Schaltelemente zur 16-, 32- oder 64-Bit Addition/Multiplikation bauen. Die Division mit Rest einer 2ω-stelligen Zahl durch eine ω-stellige ist etwas trickreicher, folgt aber auch in ihren wesentlichen Zügen dem aus der Schule bekannten Algorithmus, der auf schrittweiser Division einer (ω +1)-stelligen durch eine ω-stellige Zahl beruht. Für einen Algorithmus zu dieser Problemstellung verweisen wir auf [Knu68], wir kommen auf den klassischen Divisionsalgorithmus auch im nachfolgenden Abschnitt noch genauer zu sprechen. Der in Abschnitt 1 vorgestellte Algorithmus basierend auf fortgesetzter Subtraktion findet hier keine Anwendung. Computerprogrammierung (Arithmetik N und Z in gängigen Programmiersprachen). In jeder Programmiersprache können wir davon ausgehen, dass für die vorhandenen Datenstrukturen für N bzw. Z die entsprechenden arithmetischen Grundoperationen zur Verfügung stehen. Diese greifen üblicherweise direkt auf die im Prozessor für fixe Wortlänge ω realisierten Operationen zurück. Die Länge der Operanden und auch des Resultats ist dabei jeweils mit ω begrenzt, und bei Addition und Multiplikation zweier ω-stelliger Zahlen kann es vorkommen, dass das Resultat nicht mehr mit ω Stellen darstellbar ist. In diesen Fällen spricht man von Überlauf (engl. overflow ), und es hängt von der konkreten Sprache ab, wie Überlauf behandelt wird. Zusätzlich zu A, M und D beruhen die klassischen Algorithmen nur auf Multiplikationen mit Potenzen der Basis, die aber nur einem Verschieben der Ziffern entspricht und deswegen auch als Elementaroperation betrachtet werden kann. Den Aufwand für die Grundoperationen beschreiben wir durch die Anzahl der notwendigen Bit-Operationen. Addition und Subtraktion benötigen O(ω), die Multiplikation O(ω 2 ) Ziffernoperationen und Verschiebungen. Die Komplexität der Algorithmen hängt demnach rein von ω und nicht von den Inputs ab, bei fixem Prozessor und damit fixer Wortlänge ist sie also konstant. Darüberhinaus sind die Algorithmen in elektronischen Schaltkreisen in der Computer-Hardware enorm effizient ausführbar. Natürliche und ganze Zahlen beliebiger Länge Für viele Anwendungen ist die oben beschriebene Darstellung bei weitem ausreichend, da ja z.B. bei 32 Bit immerhin ca. 4.3 Milliarden ganze Zahlen zur Verfügung stehen. Andererseits zeigen viele Algorithmen das Phänomen, dass beim 6. N und Z 67 Rechnen mit ganzen Zahlen deren Größe rapide zunimmt, inbesondere gilt dies für Zähler und Nenner beim Rechnen mit rationalen Zahlen, siehe Abschnitt 8, und beim Rechnen mit Polynomen, siehe Abschnitt 14, sodass man hier schnell an die Grenzen stößt, wenn man die Zahlen in ihrer Größe bzw. Stellenanzahl beschränkt. Die Idee ganzer Zahlen beliebiger Länge ist nun, aufbauend auf ganzen Zahlen einer fixen Wortlänge ω eine Basisdarstellung mit B = 2ω−1 zu verwenden, wobei die der Hardware zugrundeliegende Wortlänge ω = 16, ω = 32 oder ω = 64 Bits gewählt wird. Die Ziffern“ einer derartigen Darstellung sind dann (ω − 1)” Bit-Zahlen, die in den oben erwähnten Standard-Datenstrukturen repräsentiert werden können, für negative Zahlen wird aber lediglich die führende Ziffer negativ dargestellt. Im Unterschied zum Computerchip, in dem eine fixe Stellenanzahl unerlässlich für die effektive Umsetzung der Algorithmen in elektronischen Bauteilen ist, wird nun aber die Anzahl der Ziffern nicht fixiert, sondern es werden prinzipiell beliebig viele Ziffern zur Darstellung einer natürlichen oder ganzen Zahl herangezogen. Zur mathematischen Beschreibung der Basisdarstellung einer Zahl benötigen wir daher eine geeignete Möglichkeit, beliebig viele Objekte geordnet zu einem neuen Objekt zusammenzufassen. Mengen von Ziffern scheiden für die Darstellung beliebig großer Zahlen aufgrund des fehlenden Ordnungsbegriffs aus. Neben Mengen setzen wir auch Tupel als elementare mathematische Objekte voraus und stellen uns darunter eine geordnete Kollektion von endlich vielen Objekten vor. Als Grundoperation auf einem Tupel t betrachten wir die Bestimmung der Länge |t| und den Zugriff ti (für 1 ≤ i ≤ |t| oder manchmal auch 0 ≤ i < |t|) auf einzelne Elemente des Tupels. Wie Mengen sind Tupel nicht in ihrer Länge fixiert, sie unterstützen strukturelle Operationen zum Einfügen und Löschen von Elementen an bestimmten Positionen und zum Zusammenhängen zweier Tupel zu einem längeren Tupel. Für jedes Tupel t bezeichnen wir mit ti:j (für 1 ≤ i ≤ j ≤ |t| oder 0 ≤ i ≤ j < |t| das Tupel hti , . . ., tj i. Für i > j ist ti:j das leere Tupel hi. Zur Tupelbildung steht in Anlehnung an die Mengenschreibweise htx | x = i, . . . , ji für das Tupel htx→i , . . ., tx→j i und analog dazu htx | x ∈ Ii, sofern aus dem Zusammenhang klar ist, in welcher Reihenfolge der (endliche) Indexbereich I zu durchlaufen ist5 . Für i > j und I = ∅ beschreiben beide Konstrukte das leere Tupel hi. Beispiele dazu sind 3 2 k k = 1, . . . , 5 = h1, 4, 9, 16, 25i n n ∈ {0, 2, 4} = h0, 8, 64i. Zur Realisierung von Algorithmen nehmen wir an, dass die gewählte Programmiersprache Tupel mitsamt den eben eingeführten Grundoperationen zur Verfügung stellt. In Mathematica stehen für Tupel Listen mit einer Unzahl von Listenoperationen bereit. Mathematisch sind Tupel sehr eng verwandt mit Vektoren, siehe Kapitel III, jedoch verbindet man mit dem Begriff Vektoren vor allem arithmetische Operationen wie Addition und Multiplikation, die uns dann zum Begriff eines Vektorraumes 5 Hier steht tx für einen beliebigen Ausdruck mit freier Variable x und tx→y für den Ausdruck, der entsteht, wenn y für x eingesetzt wird. 68 Kapitel II. Zahlbereiche führen. Eher selten hingegen führt man auf Vektoren strukturelle Operationen aus wie das Zusammenhängen zweier Vektoren der Länge drei zu einem Vektor der Länge sechs. Die Begriffe Vektor und Tupel werden nicht immer streng voneinander unterschieden, auch wir wollen für Tupel der Länge n bestehend aus Elementen von Typ K die für Vektoren gebräuchliche Schreibweise K n verwenden. Definition (Zifferntupel für ganze Zahlen beliebiger Länge). Sei ω die Wortlänge des zugrundeliegenden Prozessors und B = 2ω−1 . Dann wird eine natürliche Zahl Pl i n = i=0 zi B durch ihr Zifferntupel hz0 , . . ., zl i repräsentiert. Die Zahl 0 wird durch hi dargestellt, für n 6= 0 gilt 0 ≤ zi < B = 2ω−1 für alle i und wir wählen l so, dass Bl 6= 0 ist. In dieser Form ist die Darstellung eindeutig, wir nennen sie die kanonische Form von n. Ist n kanonisch durch hz0 , . . ., zl i repräsentiert, so ist hz0 , . . ., zl−1 , −zl i die kanonische Form von −n und wir schreiben L(n) für die Länge von n. In obiger Darstellung ist L(n) = l + 1. Wir nennen diesen Bereich ganze Zahlen beliebiger Länge, im Englischen wird meist von (arbitrarily) long integers gesprochen6 . Computerrepräsentation (Datenstrukturen). Zum Aufbau neuer mathematischer Objekte werden Datenstrukturen herangezogen. Darunter wollen wir eine Möglichkeit verstehen, Daten strukturiert zusammenzufassen und abzuspeichern. Für jede Datenstruktur brauchen wir einen Konstruktor, um aus den Einzelteilen ein neues Objekt zusammenzubauen, und Selektoren, um auf die Einzelteile eines Objektes zugreifen zu können. Vom Standpunkt der Mathematik reichen Mengen und Tupel aus, um damit neue Objekte zu definieren. Zur Umsetzung am Computer empfiehlt es sich aber, die Objekte in voneinander unterscheidbare Datenstrukturen zu untergliedern, sodass wir zum Beispiel verschiedene Arten von Tupel auseinanderhalten können. Beispiele dazu werden wir in Kürze sehen. Die meisten Programmiersprachen erlauben die Definition eigener Strukturen, in objektorientierten Sprachen sind etwa Klassen das geeignete Mittel. Computerrepräsentation (Datenstruktur Z für ganze Zahlen beliebiger Länge). Für ganze Zahlen beliebiger Länge führen wir eine Datenstruktur Z ein, in der wir das zugehörige Zifferntupel abspeichern. Den Konstruktor nennen wir dabei ebenfalls Z, und als Selektor halten wir uns ziff zum Zugriff auf das Zifferntupel zur Verfügung7 . y 6 In [Knu69] wird die Bezeichnung multi-precision arithmetic verwendet, was in der deutschen Übersetzung zu mehrfachgenauer Rechnung und zu Zahlen mit erweiterter Genauigkeit wird. Das erklärt sich dadurch, dass die dort beschriebenen Konzepte nicht auf ganze Zahlen beschränkt sind, sondern sich im Prinzip auch auf (Gleit-)Kommazahlen anwenden lassen, die wir in Abschnitt ?? behandeln. In dem für uns interessanten Fall von ganzen Zahlen verbirgt sich jedoch hinter der Ziffernliste beliebiger Länge eine Ausdehnung des darstellbaren Bereichs und weniger eine Erhöhung der Genauigkeit. 7 Als Schreibweise in mathematischen Algorithmen vereinbaren wir für Konstruktoren etwa z ← Z(h. . .i), um ein Objekt z in der gewünschten Datenstruktur zu kreieren, alternativ dazu verwenden wir Z(ziff ← h. . .i). Zweiteres ist insbesondere dann übersichtlich, wenn die Struktur aus mehreren Komponenten besteht. Zum Zugriff auf die Daten kann der Selektor einfach auf die Struktur angewendet werden, also z.B. ziff(z), um auf das Zifferntupel von z zuzugreifen. 6. N und Z 69 Beispiel. Mit Wortlänge ω = 32 stellt z = h67890, 12345, 54321i ∈ Z die Zahl 67890 + 12345 · 231 + 54321 · 262 = 250511396233504824035634 zur gewohnten Basis 10 dar. Deren Negatives ist h67890, 12345, −54321i, und eine Rechtsverschiebung des Tupels zu h0, 67890, 12345, −54321i entspricht der Multiplikation mit B = 231 . Bei einer Linksverschiebung des Zifferntupels zu q = h12345, 54321i geht die niedrigste Ziffer r = z0 = 67890 verloren. Die daraus resultierenden q = 116653459255353 und r = 67890 entsprechen Quotient und Rest bei Division von z durch B. y Wenden wir uns nun den klassischen Algorithmen für die arithmetischen Grundoperationen auf Z zu. Diese lassen sich auf beliebig langen ganzen Zahlen ohne wesentliche Modifikationen übertragen, wenn man sich als Basis der Zahlendarstellung nun B = 2ω−1 denkt. Die zugrunde liegenden Elementaroperationen A, M und D können hier mittels der oben besprochenen Operationen auf ω-Bit-Zahlen umgesetzt werden. Die meisten Programmiersprachen bieten Zahlen in mindestens zwei verschiedenen Längen an. Seien etwa int und long die verfügbaren Datenstrukturen für 32-Bit und 64-Bit Integers, dann wählen wir B = 2ω−1 = 231 als Basis für eine long integer Arithmetik. D steht dann meist direkt durch eine vorhandene Division für long durch int zur Verfügung, und M kann man durch Konversion der int-Operanden in long und Berechnung eines long-Resultat realisieren. Für A nutzen wir aus, dass die Operanden kleiner als 231 sind, sodass deren int-Addition keinen Überlauf produzieren kann. Das geforderte 31-Bit Resultat und den Übertrag ermittelt man durch Division mit Rest durch 231 , was gegebenenfalls durch reines Verschieben der Bits in effizienter Weise erreicht werden kann. Die klassischen Algorithmen zur Addition und Multiplikation beruhen auf Addieren und Multiplizieren von Ziffern als Elementaroperationen. Bei der Division mit Rest von a durch b hingegen werden schrittweise die Ziffern des Quotienten durch Division einer (L(b) + 1)-stelligen Zahl durch die L(b)-stellige Zahl b berechnet. Eine solche Operation steht jedoch als Elementaroperation noch nicht zur Verfügung. Interessanterweise lässt sich aber in jedem Divisionsschritt die gesuchte Ziffer des Quotienten durch Division mit Rest der aus den beiden führenden Ziffern des Dividenden gebildeten 2-stelligen Zahl durch die führende Ziffer des Divisors berechnen. Somit haben wir nun einen Divisionsalgorithmus, in dem wir nur auf die zugrundeliegenden Elementaroperationen A, M und D zurückgreifen. Für Details, die bei einer Realisierung der klassischen Algorithmen für beliebig lange Zahlen zu beachten sind, verweisen wir wieder auf [Knu68]. Wir setzen von nun an also Algorithmen QuotRestN (bzw. QuotRestZ) zur simultanen Berechnung von Quotient und Rest, QuotN (bzw. QuotZ) zur Berechnung des Quotienten und RestN (bzw. RestZ) zur Berechnung des Restes voraus. Liegen nun zwei Zahlen a, b ∈ Z vor, so liefert a + b ein Ergebnis mit maximaler Länge max(L(a), L(b)) + 1 (anstelle eines Resultats plus Übertrag), und a · b ergibt eine Zahl mit maximaler Länge L(a) + L(b). Auf diese Weise können durch 70 Kapitel II. Zahlbereiche elementare Rechenoperationen beliebig große Zahlen erzeugt werden, und diese können ohne eine konzeptionelle Einschränkung betreffend ihrer Größe am Rechner auch dargestellt werden. Praktisch gilt natürlich auch hier eine Beschränkung, nämlich durch den auf einem Computer momentan verfügbaren Speicherplatz. Computerprogrammierung. In Computeralgebra-Systemen wie Mathematica oder Maple wird standardmäßig mit Integern beliebiger Länge gerechnet, in Matlab sind sie über die Symbolic Toolbox ebenfalls verfügbar. Herkömmliche Programmiersprachen bieten jedoch keinen Grunddatentyp für Integer beliebiger Länge, allenfalls sind zusätzliche Programm-Bibliotheken erhältlich. Die fehlende Längenbeschränkung hat zur Folge, dass die klassischen Algorithmen nicht mehr in elektronischen Schaltkreisen – der Hardware – realisiert werden können, sondern dass sie in Form von Computerprogrammen in Software umgesetzt werden müssen. Dies kann bei der long integer Arithmetik zu (deutlich) längeren Rechenzeiten im Vergleich zu direkt im Prozessor ablaufenden Berechnungen führen. Auf Seite 66 haben wir für die klassischen Algorithmen auf Zahlen fixer Wortlänge konstanten Aufwand an Bit-Operationen festgestellt. Für Zahlen beliebiger Länge beschreiben wir nun die Komplexität der arithmetischen Grundoperationen durch die benötigten Elementaroperationen auf ω-Bit-Zahlen. Dabei ziehen wir die Länge der Operanden als Maß für die Größe der Eingaben heran. Für die Addition benötigt man dann im Durchschnitt O(min(L(a), L(b))) Elementaroperationen. Für die Multiplikation benötigt der klassische Algorithmus L(a) · L(b) elementare Multiplikationen und L(b) Additionen von Zahlen, deren kürzere eine Länge L(a) aufweist, insgesamt O(L(a) · L(b)) Elementaroperationen. Zur Division mit Rest von a durch b werden maximal O(L(b) · (L(a) − L(b) + 1)) elementare Operationen benötigt. Wir wollen abschließend die Multiplikation zweier Zahlen a und b etwas genauer unter die Lupe nehmen. Sei nun max(L(a), L(b)) ≤ 2n. Wir zerlegen dazu sowohl a als auch b in zwei Hälften“, genauer gesagt ” b̄ := bn:L(b)−1 . ã := a0:n−1 ā := an:L(a)−1 bzw. b̃ := b0:n−1 Für das Produkt der beiden Zahlen gilt dann a · b = (ã + B n ā) · (b̃ + B n b̄) = ãb̃ + B n ((ã + ā)(b̃ + b̄) − ãb̃ − āb̄) + B 2n āb̄, und wir brauchen nur drei Produkte zweier jeweils maximal n-stelligen Zahlen berechnen, nämlich ã· b̃, ā· b̄ und (ã+ā)(b̃+ b̄). Dies führt direkt auf einen rekursiven Divide-and-Conquer-Algorithmus, den sogenannten Karatsuba8 Algorithmus zur Multiplikation ganzer Zahlen. Um den Algorithmus in seinen Details etwas zu vereinfachen, empfiehlt es sich, a und b am Beginn mit Nullen auf eine geeignete Länge l = 2k aufzufüllen. Wir konzentrieren uns im Algorithmus MultZKaratsuba auf den Spezialfall der Multiplikation strikt positiver Zahlen, die Behandlung des Vorzeichens und Multiplikation mit 0 ist einfach und bleibt dem Leser überlassen. 8 Karatsuba, Anatolii Alexeevich: 1937–, http://www.mi.ras.ru/~karatsuba/index e.html 71 6. N und Z Algorithmus MultZKaratsuba: Karatsuba Algorithmus in Z if max(L(a), L(b)) = 1 p←a∗b else k ← ⌈log2 (max(L(a), L(b)))⌉, l1 ← 2k−1 , l2 ← 2k a, b ← Fülle(a, 2k ), Fülle(b, 2k ) ã ← a0:l1−1 , ā ← al1:l2−1 b̃ ← b0:l1−1 , b̄ ← bl1:l2−1 p1 ← MultZKaratsuba(ã, b̃) p2 ← MultZKaratsuba(ā, b̄) p3 ← MultZKaratsuba(ã + ā, b̃ + b̄) s1 ← Verschiebe(p3 − p1 − p2, l1) s2 ← Verschiebe(p2, l2) p ← p1 + s1 + s2 return p Aufruf: Eingabe: mit: Ausgabe: mit: MultZKaratsuba(a, b) a, b ∈ Z a, b > 0 p∈Z p = a · b. Verwendete Unteralgorithmen Fülle(n, d) füllt n rechts mit 0 auf Gesamtlänge d auf. Verschiebe(n, d) verschiebt die Ziffern von n um d Stellen nach rechts und füllt n mit 0 auf. Computerprogrammierung. In einer konkreten Realisierung des Algorithmus ist zu berücksichtigen, dass ã ā, b̃ und b̄ nicht notwendigerweise in kanonischer Form sind. Dies ist vor allem dann relevant, wenn die Unteralgorithmen zum Addieren und Subtrahieren die Operanden in kanonischer Form erwarten. Mit dem Karatsuba Algorithmus führen wir die Multiplikation zweier maximal n-stelliger Zahlen auf drei Multiplikationen jeweils maximal n/2-stelliger Zahlen, einige Additionen maximal n-stelliger Zahlen und einige Verschiebungen der Ziffern um maximal n Stellen zurück. Der Aufwand für die Additionen und Verschiebungen ist jeweils O(n), also durch c · n mit c ∈ R+ beschränkt. Eine obere Schranke für den Gesamtaufwand M (n) zur Multiplikation zweier Zahlen der Länge n ist demnach durch M (n) = 3M (n/2) + c · n (6.2) gegeben. Eine Lösung M (n) dieser Rekurrenzgleichung ausgedrückt rein durch n kann man z.B. in Mathematica durch den Befehl In[1]:= RSolve[{M (n) == 3M (n) + cn, M (1) == 1}, M (n), n] Log[n] Out[1]= {{M (n) → (2c + 1)3 Log[2] − 2cn}} finden. Dies besagt, dass die durchschnittliche Anzahl von Elementaroperationen Log[n] durch (2c + 1)3 Log[2] − 2cn beschränkt, die durchschnittliche Komplexität somit Log[n] O(3 Log[2] ) = O(nlog2 (3) ) = O(n1.585 ) ist. Der klassische Algorithmus benötigt im Vergleich O(n2 ) Elementaroperationen, jedoch zahlt sich der zusätzliche Aufwand für Additionen, Verschiebungen 72 Kapitel II. Zahlbereiche und Rekursion erst für sehr lange Zahlen aus. Der Karatsuba Algorithmus ist darüberhinaus ein Beispiel eines Verfahrens, das mit rekursiven Aufrufen sehr leicht und elegant zu formulieren ist, eine Überführung in einen Schleifenalgorithmus jedoch nicht in naheliegender Weise ermöglicht. Der Rechenaufwand für die arithmetischen Grundoperationen auf ganzen Zahlen beliebiger Länge hängt also ganz wesentlich von der Größe der Operanden ab. Viele Komplexitätsaussagen über Algorithmen auf anderen mathematischen Datenstrukturen (Vektoren, Matrizen, Polynome, etc.) beruhen jedoch auf der vereinfachenden Annahme von konstantem Aufwand für elementare Arithmetik und beschreiben daher den Aufwand lediglich durch die Anzahl von arithmetischen Operationen. Die Größe der zu verarbeitenden Zahlen wird dabei vernachlässigt, insbesondere auch das Phänomen, dass Zwischenergebnisse während der Abarbeitung der Algorithmen enorm anwachsen“ können, sodass solche Aspekte zusätzlich ” zu einem theoretischen Komplexitätsresultat als Beurteilungskriterium für Algorithmen herangezogen werden sollten. Von nun an werden wir in Algorithmen, die mit beliebig langen natürlichen oder ganzen Zahlen rechnen, der Einfachheit halber auf einer bestehenden Arithmetik für Zahlen beliebiger Länge aufbauen, wie sie etwa in Mathematica oder der Symbolic Toolbox in Matlab zur Verfügung stehen. Das bedeutet insbesondere, dass wir Zahlen immer in der gewohnten Dezimalschreibweise anstelle eines Zifferntupels zur Basis 2w−1 anschreiben werden, d.h. für z = h67890, 12345, 54321i ∈ Z werden wir wie gewohnt 250511396233504824035634 schreiben, und wir werden immer Z verwenden, ohne auf konkrete Datenstrukturen für Z einzugehen. Die bisher vorgestellten Algorithmen sollen lediglich veranschaulichen, was in Systemen wie Mathematica oder Matlab vorgeht, wenn mit derart großen Zahlen gerechnet wird. Der Euklidsche Algorithmus Das Bestimmen des größten gemeinsamen Teilers (ggT) zweier Zahlen ist eine der zentralen Problemstellungen in N bzw. Z. Auch in anderen Gebieten – z.B. beim Rechnen mit Brüchen – werden wir dieser Fragestellung wieder begegnen. Wir rekapitulieren zuerst einige grundlegende Begriffe, bevor wir uns einem Verfahren zur ggT-Berechnung zuwenden. Definition (Teilbarkeit in Z). Seien a, b, t ∈ Z. Die Zahl t teilt a genau dann, wenn ein q ∈ Z existiert, sodass a = tq. Wir schreiben dafür t|a und nennen t einen Teiler von a. Gilt t|a und t|b, so ist t ein gemeinsamer Teiler von a und b. Wir vereinbaren T (a) := {t ∈ Z | t|a} und T (a, b) := {t ∈ Z | t|a ∧ t|b}. Ist a 6= 0 oder b 6= 0, dann ist die Menge T (a, b) der gemeinsamen Teiler von a und b endlich und hat daher ein größtes Element bzgl. der natürlichen Ordnung ≤. Definition (ggT in Z). Seien a, b ∈ Z mit a 6= 0 oder b 6= 0. Wir nennen ggT(a, b) := max T (a, b) 73 6. N und Z den größten gemeinsamen Teiler von a und b. Weiters legen wir ggT(0, 0) := 0 fest. Problemstellung Gegeben: Gesucht: mit: (ggT in Z). a, b ∈ Z. t ∈ N0 t = ggT(a, b). Wegen ggT(a, b) = ggT(|a|, |b|) reicht es aus, die Problemstellung für m, n ∈ N0 zu untersuchen. Ist hier wiederum eine der Eingabezahlen 0, so ist die jeweils andere der ggT, somit bleiben als interessante Fälle nur a, b ∈ N übrig. Existenz des ggT ist schon geklärt worden, die Eindeutigkeit des ggT folgt aus der Eindeutigkeit des Maximums in der Definition. Das oben angeführte Argument zur Existenz des ggT ist konstruktiv : wir erhalten ggT(a, b), indem wir die endlichen Mengen T (a) und T (b) etwa durch Primfaktorzerlegung von a bzw. b berechnen und dann das Maximum der endlichen Menge T (a, b) = T (a) ∩ T (b) ermitteln. Diese Methode ist jedoch für größere a und b sehr aufwändig, man bedenke, dass die Sicherheit einiger Verschlüsselungsverfahren9 genau auf dem großen Aufwand der Faktorisierung ganzer Zahlen beruht. Der Schlüssel zu einem effizienten Algorithmus liegt in der einfachen Beobachtung, dass • ein gemeinsamer Teiler von a und b auch ein Teiler von a ± b ist, und dass • ein Teiler von b auch jedes Vielfache von b teilt. Seien nun q und r Quotient und Rest bei Division von a durch b. Ein gemeinsamer Teiler von a und b teilt auch b · q und daher auch r = a − b · q. Umgekehrt teilt ein gemeinsamer Teiler von b und r = a − b · q auch a = r + b · q. Damit ist T (a, b) = T (b, r) und letztlich auch ggT(a, b) = ggT(b, r). Diese rekursive Eigenschaft kann direkt in einen rekursiven Algorithmus zur Berechnung von ggT(a, b) umgesetzt werden, dessen Basisfall bei ggT(a, 0) = a erreicht ist. Es ist solange zu dividieren, bis als Rest bei der Division 0 auftritt, der letzte in dieser Reihe auftretende Divisor ist der ggT von a und b. Die der Reihe nach auftretenden Divisoren bilden eine streng monoton fallende Folge in N0 , daher muss die Rekursion nach endlich vielen Schritten terminieren. Computerprogrammierung. Die bei der ggT-Berechnung auftretende spezielle Form der Rekursion, in der das Resultat des rekursiven Aufrufs nicht mehr weiterverwendet wird, heißt Endrekursion (engl. tail recursion). Diese sind besonders leicht 9 Die Sicherheit des RSA Algorithmus zur Verschlüsselung elektronischer Nachrichten beispielsweise beruht darauf, dass eine genügend große Zahl z = p · q ohne vorherige Kenntnis der Primfaktoren p und q schwierig zu faktorisieren ist. 74 Kapitel II. Zahlbereiche als Schleifen realisierbar, weil im Zuge der rekursiven Aufrufe kein Stack“ aufge” baut werden muss, siehe dazu die Erläuterungen zum rekursiven Funktionsaufruf auf Seite 25. Man erkennt Endrekursion daran, dass im Algorithmus nach dem rekursiven Aufruf keine weitere Anweisung mehr auszuführen ist. Der auf diese Weise entstehende Algorithmus heißt Euklidscher10 Algorithmus, den wir hier in Form einer Schleife präsentieren, siehe GGTZEuklid. Algorithmus GGTZEuklid: Euklidscher Algorithmus für ganze Zahlen t ← |a|, r ← |b| while r 6= 0 s ← RestN(t, r), t ← r, r ← s return t Aufruf: Eingabe: Ausgabe: mit: GGTZEuklid(a, b) a, b ∈ Z. t ∈ N0 t = ggT(a, b). Eine Komplexitätsuntersuchung des Euklidschen Algorithmus gestaltet sich sehr aufwändig und auch mathematisch schwierig. Wir begnügen uns mit einer Abschätzung, die auch die Größe des Resultats miteinbezieht, wonach der oben beschriebene Algorithmus GGTZEuklid zur Berechnung von t = ggT(a, b) maximal O(min(L(a), L(b)) · (max(L(a), L(b)) − L(t) + 1)) elementare arithmetische Operationen benötigt. Beispiel. Für a und b wie im Beispiel zum Karatsuba Algorithmus liefert der Aufruf GGTZEuklid(a, b) den ggT 1, wir nennen a und b in diesem Fall relativ prim. Rufen wir nun GGTZEuklid(a, b) mit a = 250511396233504824035634 und b = 116653459255353 auf, so lauten die der Reihe nach berechneten Reste r(1) = 67890 r(2) = 59793 r(3) = 8097 r(4) = 3114 r(6) = 1245 r(7) = 624 r(8) = 621 r(9) = 3 r(5) = 1869 r(10) = 0, somit ist ggT(a, b) = 3. Die Folge |a|, |b|, r(1) , . . . , r(10) wird auch Euklidsche Restfolge genannt. Der hier gezeigte Algorithmus kann in verschiedenen Weisen verallgemeinert werden, etwa kann er wegen ggT(a, b, c) = ggT(a, ggT(b, c)) leicht zum Berechnen des ggT beliebig vieler Zahlen adaptiert werden. Eine Erweiterung, die neben dem ggT auch noch eine Darstellung des ggT als Linearkombination der Ausgangszahlen berechnet, werden wir im nachfolgenden Abschnitt kennenlernen. 10 Euklid, von Alexandria: 325 v.Chr. – 265 v.Chr. Sein Hauptwerk sind Die Elemente, die eine axiomatische Herangehensweise an die Geometrie darstellen. Darin enthalten sind auch die Grundlagen der Zahlenthorie, wie Beispielsweise Teilbarkeit und ggT. 75 6. N und Z Das Lösen von Gleichungen in Z Gleichungen mit ganzzahligen Koeffizienten, in denen ganzzahlige Lösungen gesucht sind, heißen allgemein diophantische11 Gleichungen, etwa x2 + y 2 = z 2 (6.3) für gesuchte x, y, z ∈ Z. Geometrisch interpretiert sind hier ganze Zahlen gesucht, die als Seitenlänge eines rechtwinkeligen Dreiecks auftreten können, wobei man naturgemäß an positiven Lösungen interessiert ist. Die Gleichung (6.3) besitzt unendlich viele Lösungen, genannt pythagoräische12 Tripel. Die Verallgemeinerung von (6.3) xn + y n = z n für n ∈ N ist ebenfalls eine diophantische Gleichung, sie besitzt jedoch für n > 2 nach dem großen Fermat’schen13 Satz keine Lösungen x, y, z ∈ Z. Wir wollen uns nun linearen diophantischen Gleichungen, das sind solche, die keine Potenzen von Unbekannten enthalten, zuwenden und hier insbesondere den Fall von Gleichungen mit zwei Unbekannten studieren. Problemstellung (Lineare diophantische Gleichung in zwei Unbekannten). Gegeben: a, b, c ∈ Z. Gesucht: x, y ∈ Z mit: ax + by = c. Für eine Untersuchung der Existenz von Lösungen einer diophantischen Gleichung bemerken wir zuerst, dass natürlich immer ggT(a, b)|(ax + by) gilt. Im Falle der Lösbarkeit der diophantischen Gleichung muss damit auch ggT(a, b)|c gelten. Sei nun umgekehrt ggT(a, b)|c erfüllt, also c = q · ggT(a, b). Falls die Gleichung ax + by = ggT(a, b) (6.4) lösbar ist, d.h. ax′ + by ′ = ggT(a, b), dann ist mit x = qx′ und y = qy ′ (6.5) eine Lösung der ursprünglichen diophantischen Gleichung gefunden. Zur Lösung von (6.4) studieren wir die vom Euklidschen Algorithmus berechnete Restfolge |a|, |b|, r(1) , . . . , r(k) = ggT(a, b), 0. Die ersten zwei Werte der Restfolge sind wegen |a| = a · sgn(a) + b · 0 und b = a · 0 + b · sgn(b) trivial als Linearkombination von a und b darstellbar. Kann nun 11 Diophant, von Alexandria: ca. 200 n.Chr.–280 n.Chr., griechischer Mathematiker. von Samos: ca. 570 v.Chr.–475 v.Chr., griechischer Mathematiker und Phi- 12 Pythagoras, losoph. 13 Fermat, Pierre de: 1601–1665. 76 Kapitel II. Zahlbereiche sowohl r(i−2) als auch r(i−1) als Linearkombination von a und b dargestellt werden, also r(i−2) = ax(i−2) + by (i−2) r(i−1) = ax(i−1) + by (i−1) , dann kann wegen r(i) = r(i−2) − qr(i−1) = ax(i−2) + by (i−2) − q(ax(i−1) + by (i−1) ) = a(x(i−2) − qx(i−1) ) + b(y (i−2) − qy (i−1) ) (6.6) auch r(i) als Linearkombination von a und b geschrieben werden. Wir können also im Euklidschen Algorithmus zusätzlich Koeffizienten x und y wie in (6.6) beschrieben mitrechnen, mit denen beim Abbruch des Algorithmus eine Darstellung von r(k) = ggT(a, b) als Linearkombination ax(k) + by (k) – und damit auch eine Lösung von (6.4) – gegeben ist. Diese Variante des Verfahrens wird erweiterter Euklidscher Algorithmus genannt. Algorithmus ErwGGTZEuklid: Erweiterter Euklidscher Algorithmus in Z t ← |a|, r ← |b| x ← sgn(a), y1 ← sgn(b), y, x1 ← 0 while r 6= 0 q, s ← QuotRestN(t, r), t ← r, r ← s x2 ← x − qx1, y2 ← y − qy1 x, y ← x1, y1, x1, y1 ← x2, y2 return t, x, y Aufruf: Eingabe: Ausgabe: mit: ErwGGTZEuklid(a, b) a, b ∈ Z. t ∈ N0 , x, y ∈ Z t = ggT(a, b) und t = ax + by. Beispiel. Mit a und b wie im Beispiel oben berechnet der erweiterte Euklidsche Algorithmus eine Darstellung a · 187291604932 + b · (−402205658999146152045) = 3 (6.7) des ggT als Linearkombination der Ausgangswerte. Satz (Lösbarkeit einer linearen diophantischen Gleichung). Seien a, b, c ∈ Z. Dann gilt: Die Gleichung ax + by = c ist lösbar für x, y ∈ Z ⇐⇒ ggT(a, b)|c. Im Fall der Lösbarkeit existieren jedoch automatisch unendlich viele Lösungen, da mit jeder Lösung x, y z.B. auch x + b und y − a eine Lösung der Gleichung darstellt. Zudem sind die Eingabe- und Ausgabevariablen in dieser Aufgabenstellung keine reellen Zahlen, sodass jegliche Überlegungen hinsichtlich der Kondition das Problems Lösen einer linearen diophantischen Gleichung“ hinfällig sind. ” Die oben angestellten Überlegungen zur Existenz von Lösungen einer diophantischen Gleichung führen nun unmittelbar zu einem Lösungsverfahren basierend auf (6.5) und dem erweiterten Euklidschen Algorithmus. 77 7. Zm Algorithmus LöseLinDiophant: Lösen einer linearen diophantischen Gleichung in zwei Unbekannten if a = b = c = 0 x, y ← 0 else t, x′ , y ′ ← ErwGGTZEuklid(a, b) q ← QuotZ(c, t) x ← qx′ , y ← qy ′ return x, y Aufruf: Eingabe: mit: Ausgabe: mit: LöseLinDiophant(a, b, c) a, b, c ∈ Z ggT(a, b)|c. x, y ∈ Z ax + by = c. Beispiel. Für eine Lösung der diophantischen Gleichung ax − by = −6 mit a und b wie oben berechnet LöseLinDiophant(a, −b, −6) zuerst die Darstellung (6.7) für ggT(a, −b) = 3. Da 3| − 6 hat die Gleichung Lösungen, eine davon erhalten wir mit q = −2 aus (6.7) als x = −374583209864 und y = −804411317998292304090. 7 Zm Bei der Darstellung ganzer Zahlen auf einem Computer haben wir Datenstrukturen kennengelernt, mit denen etwa der Bereich zwischen 0 und 2ω − 1 darstellbar ist. Beim Rechnen mit diesen Zahlen kann Überlauf auftreten, wenn das Resultat nicht mehr im darstellbaren Bereich liegt, so ergibt etwa (2ω − 1) + 2 als Resultat 1. Das korrekte Ergebnis 2ω + 1 stimmt mit 1 jedoch modulo 2ω überein, d.h. bei Division durch 2ω haben 2ω + 1 und 1 gleichen Rest. Mathematische Grundlagen Aufbauend auf der Teilbarkeitsrelation auf Z können wir Kongruenz modulo m definieren. Definition (Kongruenz modulo n). Sei m ∈ N. Die Zahlen a, b ∈ Z heißen kongruent modulo m genau dann, wenn gilt: m|(a − b). Wir schreiben dafür a ≡m b. Es lässt sich einfach nachrechnen, dass ≡m eine Kongruenzrelation auf Z ist, d.h. eine Äquivalenzrelation auf Z, die mit Addition und Multiplikation auf Z verträglich ist im Sinne von a ≡m b =⇒ (a + a′ ≡m b + b′ und a · a′ ≡m b · b′ ). (7.8) a′ ≡ m b ′ Definition (Kongruenzklassen, Rechnen modulo m). Die Äquivalenzklasse von a bzgl. ≡m wird auch Restklasse von a modulo m genannt und mit [a]m bezeichnet. Weiters ist Zm := [a]m a ∈ Z , 78 Kapitel II. Zahlbereiche und in Zm heißt m der Modul. Auf Zm können durch [a]m · [b]m := [a · b]m [a]m + [b]m := [a + b]m selbst wieder eine Addition und eine Multiplikation definiert werden, die wegen (7.8) wohldefiniert sind, d.h. die Resultate hängen nicht von den konkret gewählten Repräsentanten der Klassen ab. y Beispiel. In Z3 lauten die Klassen [0]3 := {. . . , −6, −3, 0, 3, 6, . . . } [1]3 := {. . . , −5, −2, 1, 4, 7, . . . } [2]3 := {. . . , −4, −1, 2, 5, 8, . . . }. Wegen a ≡3 a + 3 existieren keine weiteren (von diesen verschiedene) Klassen, und jede Klasse hat die Gestalt [a]3 = {a + k · 3 | k ∈ Z}. Addition und Multiplikation verhalten sich laut Definition wie in den folgenden Verknüpfungstafeln gezeigt. + [0]3 [1]3 [2]3 [0]3 [0]3 [1]3 [2]3 [1]3 [1]3 [2]3 [0]3 · [0]3 [1]3 [2]3 [2]3 [2]3 [0]3 [1]3 [0]3 [0]3 [0]3 [0]3 [1]3 [0]3 [1]3 [2]3 [2]3 [0]3 [2]3 [1]3 Das Rechnen mit Restklassen kann auch rein auf den Repräsentanten betrachtet werden, weshalb die Klassenschreibweise oft weggelassen wird und etwa statt [2]3 · [2]3 = [4]3 = [1]3 nur kurz 2·2 =1 (mod 3) geschrieben wird14 . y Aus den Verknüpfungstafeln von Z3 sieht man sofort, dass jedes Element außer [0]3 bzgl. der Multiplikation invertierbar ist. In Z4 hingegen ist [2]4 wegen [2]4 · [1]4 = [2]4 [2]4 · [2]4 = [0]4 [2]4 · [3]4 = [2]4 nicht invertierbar bzgl. der Multiplikation. Wann ist nun eine Zahl b 6= 0 invertierbar modulo m, anders gefragt: wann hat die Gleichung bx = 1 (mod m) eine Lösung? Nun ist aber bx = 1 (mod m) ⇐⇒ bx − km = 1 ⇐⇒ ggT(b, m) · (b′ x − km′ ) = 1. (7.9) Daraus ist sofort abzulesen, dass b modulo m im Fall ggT(b, m) 6= 1 nicht invertierbar sein kann. Umgekehrt ist b modulo m im Fall ggT(b, m) = 1 invertierbar, da dann die laut (7.9) gesuchten x und k mit bx − km = 1 durch den erweiterten Euklidschen Algorithmus berechnet werden können. 14 Ein Zusatz wie (mod m)“ hinter einer Gleichheit zeigt an, dass es sich um eine Gleichheit ” von Restklassen handelt, nicht um eine Gleichheit von Zahlen, oder anders gesagt, dass anstelle von =“ eigentlich ≡m“ gemeint ist. ” ” 7. Zm 79 Satz (Restklassenring und -körper). Sei m ∈ N und m ≥ 2. Dann bildet Zm mit + und · einen Ring15 . Ist p prim, dann ist Zp mit + und · sogar ein Körper16 . Darstellung am Computer Allgemein lauten in Zm die Restklassen [a]m = {a+k·m | k ∈ Z}. Das Rechnen mit Restklassen kann durch das Rechnen mit Repräsentanten der Klassen ersetzt werden, sofern aus jeder Klasse ein eindeutig bestimmter kanonischen Repräsentant gewählt wird. Dies geschieht in der Regel durch einen sogenannten kanonischen Simplifikator. Computerrepräsentation (Datenstrukturen für Restklassen). Zm besteht aus m Restklassen, die wir durch ihre kanonischen Repräsentanten darstellen. 1. Wählt man dabei aus jeder Restklasse den kleinsten nicht-negativen Wert als kanonische Form, so ist {0, . . . , m − 1} die Menge der möglichen Repräsentanten, wir nennen diese Datenstruktur Z+ m . Der kanonische Simplifikator dazu lautet klarerweise RestZ(., m), d.h. die kanonische Form von a in Z+ m wird als Rest bei Division von a durch m berechnet. 2. Als interessante Alternative dazu bietet es sich manchmal an, die m Rem präsentanten als {−⌊ m−1 2 ⌋, . . . , 0, . . . , ⌊ 2 ⌋} möglichst symmetrisch um 0 anzuordnen, wir nennen diese Struktur nun Z± m . Als kanonischen Simplifikator hierfür verwendet man ( RestZ(a, m) falls RestZ(a, m) ≤ ⌊ m 2⌋. kanonischZ± (a) := m RestZ(a, m) − m sonst Die Konstruktoren müssen durch Aufruf des jeweiligen Simplifikators dafür sorgen, dass in den Datenstrukturen immer kanonische Repräsentanten verwendet werden. Als Selektoren stellen wir in beiden Strukturen jeweils rep und mod zum Zugriff auf den Repräsentanten und den Modul zur Verfügung. y Vereinbarung (Mathematische Objekte vs. Datenstrukturen). Prinzipiell wollen wir ein mathematisch abstrakt definiertes Objekt – wie Zm – und seine konkreten ± Computermodelle – wie Z+ m und Zm – auseinanderhalten. Stehen aber nun aber für einen abstrakten Bereich D mehrere Datenstrukturen zur Realisierung bereit, so wollen wir im algorithmischen Kontext die abstrakte Bezeichnung D auch als Synonym für ein (beliebiges) Computermodell von D“ verwenden. Schreiben wir ” also in einem Algorithmus gegeben z ∈ Z7“, so lassen wir offen, ob z in Z+ 7 oder ” ± Z7 repräsentiert sein soll. Schreiben wir hingegen gegeben z ∈ Z± “, so deutet 7 ” 15 Ein Ring ist ein Rechenbereich, in dem addiert, subtrahiert und multipliziert werden kann, und wo alle gewohnten Rechengesetze wie Kommutativität, Assoziativität und Distributivität gelten. 16 Ein Körper ist ein Ring, in dem zusätzlich durch jedes Element außer 0 dividiert werden kann. 80 Kapitel II. Zahlbereiche dies darauf hin, dass der entsprechende Algorithmus nur unter Verwendung der angezeigten Datenstruktur funktioniert oder es zumindestens empfehlenswert ist, das genannte Modell zu verwenden. y Gerechnet wird modulo m nun, indem mit Repräsentanten wie in Z gerechnet wird, und am Ende das Resultat in kanonische Form gebracht wird. Dies ist zugleich ein allgemeines Rezept, wie man das Rechnen mit Äquivalenzklassen einer Menge M durch das Rechnen mit Repräsentanten R ⊆ M und einen kanonischen Simplifikator K realisieren kann: zu jeder Operation ◦M auf M kann die entsprechende Operation ◦R auf Repräsentanten der Klassen als a ◦R b := K(a ◦M b) definiert werden. In dieser Form kann das Rechnen mit Restklassen auch leicht ± auf den eben eingeführten Datenstrukturen Z+ m oder Zm realisiert werden. Exemplarisch führen wir nun einen Algorithmus zum Multiplizieren in Z+ m an. Algorithmus ·“: Restklassenmultiplikation ” A ← rep(a), B ← rep(b) Aufruf: m ← mod(a) Eingabe: p ← Z+ Ausgabe: m (A · B) return p mit: a·b a, b ∈ Z+ m p ∈ Z+ m p = a · b. Computerprogrammierung (Polymorphismus). Unter Polymorphismus verstehen wir die Möglichkeit, mehrere Definitionen für einen Funktionsnamen zuzulassen. Im Beispiel der Restklassenmultiplikation tritt Polymorphismus in natürlicher Weise auf, da wir ·“ sowohl für die Multiplikation von Restklassen als auch für ” die Multiplikation in Z verwenden. Das Multiplizieren ganzer Zahlen ist selbstverständlich eine ganz andere Operation als das Multiplizieren von Klassen, wir erlauben uns aber jeweils, von Multiplikation“ zu sprechen und dafür den Opera” tor ·“ zu schreiben. Die Multiplikation ist damit polymorph, und wir sprechen vom ” Überladen des Operators ·“. Nicht alle Sprachen unterstützen Polymorphismus, ” im objektorientierten Programmieren jedoch ist es Standard und auch Mathematica erlaubt polymorphe Operationen. In Zm kann jedes zu m relativ prime b bzgl. der Multiplikation invertiert werden, in diesem Fall können wir also eine Division17 a durch b durchführen. In Zp mit p prim ist das Dividieren somit durch alle b 6= 0 möglich. Wir haben schon in (7.9) gesehen, dass für das gesuchte Inverse x zu b modulo m gelten muss bx − km = 1. Gilt nun für b und m wie gefordert ggT(b, m) = 1, so berechnet ErwGGTZEuklid(b, m) neben dem ggT genau die gesuchten Koeffizienten x und −k. 17 Division Rest. nun im Sinne einer Umkehrung der Multiplikation, nicht im Sinne von Division mit 81 7. Zm Algorithmus InversModEuklid: Restklasseninversion B ← rep(b), m ← mod(b) t, x, −k ← ErwGGTZEuklid(B, m) x ← Z+ m (x) return x Aufruf: Eingabe: mit: Ausgabe: mit: InversModEuklid(b) b ∈ Z+ m ggT(b, m) = 1 x ∈ Z+ m b · x = 1 (mod m). Beispiel. Für b = [871]7919 soll das multiplikative Inverse b−1 (mod 7919) berechnet werden. Dazu stellen wir b zuerst in einem Computermodell von Z7919 dar, etwa durch b ← Z+ 7919 (871). Ein Aufruf a ← InversModEuklid(b) liefert nun 7028 (in Z+ ), zur Kontrolle ergibt a · b nach obigem Algorithmus tatsächlich 1 (in Z+ 7919 7919 ). Verwenden wir anstelle dessen das Modell Z± , so ergibt a ← InversModEuklid(b) 7919 ± jetzt −891 (in Z± 7919 ) und a · b wieder 1 (in Z7919 ). Systeme von Kongruenzen in Z Wir wollen nun spezielle Systeme von Gleichungen in Restklassenbereichen betrachten, in denen z gesucht ist mit z = r1 (mod m1 ) ... z = rm (mod mn ). (7.10) Dieses Problem kann als Gleichungssystem für Restklassen“ angesehen werden, ” jedoch ist in dieser Formulierung des Problems unklar, aus welchem Bereich das gesuchte z stammen soll, da z als Restklasse bzgl. verschiedener Moduln mi beschrieben ist. Das System (7.10) kann aber auch als System von Kongruenzen formuliert werden. Problemstellung (Chinesisches Restproblem der Dimension n). Gegeben: r, m ∈ Zn mit: ggT(mi , mj ) = 1 für i 6= j. Gesucht: z ∈ Z mit: z ≡mi ri für i = 1, . . . , n. Über die Lösbarkeit des chinesisches Restproblems gibt ein Satz Aufschluss, der schon den alten Griechen und Chinesen bekannt war. Satz (Chinesischer Restsatz). Seien r1 , . . . , rn , m1 , . . . , mn ∈ Z und die Moduln mi paarweise relativ prim, d.h. ggT(mi , mj ) = 1 für i 6= j. Dann existiert ein z ∈ Z mit (7.11) z ≡mi ri für i = 1, . . . , n. Ist z eine Lösung von (7.11), dann ist auch z + k · m1 · . . . · mn für k ∈ Z eine Lösung. 82 Kapitel II. Zahlbereiche Der chinesische Restsatz garantiert uns die eindeutige Lösbarkeit des chinesisches Restproblems modulo m1 · . . . · mn , für einen Beweis des Satzes verweisen wir jedoch auf [GCL93]. Für einen Lösungsalgorithmus des chinesischen Restproblems studieren wir vorerst einmal den Spezialfall der Dimension 2. Sei also z eine Lösung der ersten Kongruenz, d.h. z = m1 x + r1 . Soll z auch die zweite Kongruenz erfüllen, so muss m1 x+r1 = m2 y+r2 sein, wir suchen also eine wegen ggT(m1 , m2 ) = 1 existierende Lösung der diophantischen Gleichung m1 x − m2 y = r2 − r1 . Wir könnten nun zur Lösung dieser Gleichung direkt LöseLinDiophant heranziehen, eine allgemeiner anwendbare Methode erhalten wir jedoch, wenn wir den erweiterten Euklidschen Algorithmus direkt einsetzen. Wir ermitteln erst ein c mit m1 c − m2 y ′ = 1, also das Inverse vom m1 modulo m2 , berechnen daraus x = (r2 − r1 )c und letztlich das gesuchte z = m1 x + r1 . Algorithmus CRA2Z: Chinesischer Restalgorithmus für 2 Kongruenzen in Z r1 ← rep(Z+ m1 (r1 )) c ← InversModEuklid(Z+ m2 (m1 )) + x ← (Z+ m2 (r2 ) − Zm2 (r1 )) · c z ← m1 · rep(x) + r1 return z Aufruf: Eingabe: mit: Ausgabe: mit: CRA2Z(r, m) r, m ∈ Z2 ggT(m1 , m2 ) = 1. z∈Z z ≡m1 r1 und z ≡m2 r2 und 0 ≤ z < m1 m2 . In CRA2Z kann als r1 die kanonische Form des gegebenen r1 in Zm1 herangezogen werden. Die Berechnung von x kann in Zm2 durchgeführt werden, da jedes zu x modulo m2 kongruente x′ = km2 + x im Ergebnis auf z ′ = m1 (km2 + x) + r1 = km1 m2 + (m1 x + r1 ) führt, sodass z ′ ≡m1 m2 z ist. Erst die letzte Berechnung von z ist in Z durchzuführen, sie ergibt zwangsläufig ein Resultat zwischen 0 und m1 m2 , das wir auch als kanonische Form von z in Z+ m1 m2 betrachten können. In manchen Anwendungen ist man an einem Resultat in Z± m1 m2 “ interessiert, was einfach zu erreichen ” + ist, indem die entsprechenden Rechnungen in Z± m1 m2 statt in Zm1 m2 ausgeführt werden. Beispiel. Zur Berechnung einer ganzen Zahl z mit z ≡17 5 und z ≡31 12 berechnet CRA2Z erst das Inverse von 17 in Z31 mittels erweitertem Euklidschen Algorithmus als c = 11. Damit wird x = (12 − 5) · 11 in Z31 berechnet, also x = 15, und letztlich z = 17 · 15 + 5 = 260. Nun wollen wir uns dem chinesischen Restproblems der Dimension n zuwenden, zu dessen Lösung wir wieder nach einer rekursiven Eigenschaft des Problems suchen. Sei dazu r′ = CRA2Z(r1:2 , m1:2 ) eine Lösung der ersten beiden Kongru- 83 7. Zm enzen, dann gilt ′ z ≡ m1 m2 r z ≡mi ri für i = 3, . . . , n z ≡m1 r1 . =⇒ z ≡m2 r2 z ≡mi ri für i = 3, . . . , n (7.12) Die rechte Seite in (7.12) ist das Originalproblem der Dimension n, die linke Seite ist nun ein chinesisches Restproblem der Dimension n − 1, da unter den gegebenen Voraussetzungen auch m1 m2 und mi (für i = 3, . . . , n) paarweise relativ prim sind. Somit kann (7.12) als Grundlage für einen rekursiven Lösungsalgorithmus herangezogen werden, bei dem in jedem Rekursionsschritt die Dimension des Problems abnimmt. Als Basisfall der Rekursion dient daher der Fall n = 2, für den wir ja schon einen Lösungsalgorithmus haben. Dem oben vorgestellten CRA2Z kommt dabei doppelte Bedeutung zu: zum Einen löst er den Basisfall, zum Anderen dient er laut (7.12) auch der Dimensionsreduktion von n auf n − 1. Algorithmus CRAZ: Chinesischer Restalgorithmus in Z n ← |r| if n = 2 z ← CRA2Z(r, m) else r2 ← CRA2Z(r1:2 , m1:2 ) m2 ← m1 m2 z ← CRAZ(r2:n , m2:n ) return z Aufruf: CRAZ(r, m) Eingabe: r, m ∈ Zn mit: ggT(mi , mj ) = 1 für i 6= j, n ≥ 2. Ausgabe: z ∈ Z mit: z ≡mi ri für i = 1, . . . , n und 0 ≤ z < m1 · . . . · mn . Computerprogrammierung. Dieser Algorithmus ist ein Beispiel für eine Rekursion, deren Ablauf auch sehr einfach in Form einer Schleife zu realisieren ist, da das Resultat des rekursiven Aufrufs im Algorithmus nicht mehr weiterverarbeitet wird – es stimmt in diesem Fall mit dem Endresultat überein. Solche Rekursionen werden auch Tail Fällen entfällt der gesamte Aufbau des Stacks, und die Abarbeitung der Rekursion endet mit dem Erreichen des Basisfalls. Die zweite Phase – das Abarbeiten des Stacks – entfällt ebenso. Im Beispiel des chinesischen Restalgorithmus können etwa in einer Schleife solange die ersten beiden Moduln durch deren Produkt und die ersten beiden Reste durch die Lösung dieses chinesischen Restproblems ersetzt werden, bis nur mehr je zwei Reste und Moduln verbleiben und ein letzter Aufruf von CRA2Z die Lösung bringt. Beispiel (Fortsetzung). Gesucht sei nun eine ganze Zahl z mit z ≡17 5 z ≡31 12 z ≡23 11 z ≡28 16 z ≡41 21. Zur Lösung dieser Aufgabe rufen wir CRAZ((5, 12, 11, 16, 21) , (17, 31, 23, 28, 41)) 84 Kapitel II. Zahlbereiche auf. Die Werte von r und m zeigen während der Rekursion den folgenden Verlauf: r m (5, 12, 11, 16, 21) (17, 31, 23, 28, 41) (260, 11, 16, 21) (527, 23, 28, 41) (11327, 16, 21) (12121, 28, 41) (120416, 21) (339388, 41) An dieser Stelle ist der Basisfall der Rekursion erreicht, und durch Anwendung des chinsischen Restalgorithmus für Dimension 2 erhalten wir z = 2156744. 8 Q Wir wollen uns nun den rationalen Zahlen Q und deren Verwendung am Computer zuwenden. Rationale Zahlen werden als Erweiterung der ganzen Zahlen eingeführt, da ganze Zahlen bzgl. der Multiplikation keine Inversen in Z besitzen und daher keine Division ermöglichen. Die hier gezeigte Erweiterungskonstruktion, die von Z zu Q führt, kann aber nicht nur für ganze Zahlen durchgeführt werden, sie erlaubt allgemein die Erweiterung eines Integritätsbereichs18 R zu einem (Quotienten-)Körper. Aus algorithmischer Sicht benötigt man jedoch in R auch eine Division mit Rest, sodass größte gemeinsame Teiler mittels des Euklidschen Algorithmus berechnet werden können. Wir bilden dazu Paare von ganzen und betrachten die Relation hz, ni ∼ hz ′ , n′ i :⇔ zn′ = nz ′ . Durch einfaches Nachrechnen läßt sich eine wichtige Eigenschaft der Relation ∼ nachweisen. Satz. Die Relation ∼ ist eine Äquivalenzrelation auf Z × (Z \ {0}). Definition (Brüche und Brucharithmetik). Wir bezeichnen die Äquivalenzklasse von hz, ni bezüglich ∼ mit nz und nennen jedes nz einen Bruch (über Z) mit z als Zähler und n als Nenner. Die rationalen Zahlen sind dann definiert als z z, n ∈ Z ∧ n 6= 0 . Q := n Auf Q definieren wir Addition und Multiplikation wie folgt: z zn′ + z ′ n z′ + ′ := n n nn′ z z′ zz ′ . · ′ := n n nn′ (8.13) y 18 Ein Integritätsbereich ist ein kommutativer Ring mit Einselement ohne Nullteiler. Ein Nullteiler in R ist ein 0 6= a ∈ R, sodass für ein 0 6= b ∈ R gilt ab = 0. 85 8. Q Wie immer, wenn Operationen auf Äquivalenzklassen definiert werden, ist darauf zu achten, dass die so definierten Operationen wohldefiniert sind, siehe dazu auch die Einführung von Restklassen auf Seite 77. In diesem Fall sei der Beweis, der aus reinem Nachrechnen unter Zuhilfenahme der Definitionen besteht, dem Leser überlassen. Das multiplikative Inverse zu nz ist nz , und jedes z ∈ Z können wir mit dem Bruch z1 identifizieren, sodass man Q tatsächlich als eine Erweiterung von Z betrachten kann. In dieser Erweiterung können wir nun ganze Zahlen z und n dividieren, indem wir die Division z/n als z n −1 z 1 = · · 1 1 1 n in Q interpretieren. Nach obigen Rechenregeln erhält man als Resultat daher z/n = z n . Es lässt sich leicht zeigen, dass Q tatsächlich ein Körper ist. Computerrepräsentation (Datenstruktur für Bruchzahlen). Für die Darstellung von Brüchen am Computer haben wir bei unserer Behandlung von Restklassen in Abschnitt 7 schon wertvolle Vorarbeiten geleistet. Wie Q besteht auch Zm aus Äquivalenzklassen, und auch Brüche werden wir wie Restklassen am Computer durch kanonische Repräsentanten darstellen. Wir führen dazu eine Datenstruktur Q ein, in der wir mit zähler und nenner auf die beiden Komponenten eines Bruches zugreifen. Zähler und Nenner sind dabei als beliebig lange ganze Zahlen in der Datenstruktur Z darzustellen. Auch dem Rechnen mit kanonischen Repräsentanten sind wir auf Seite 80 schon begegnet, wir brauchen also nur noch einen kanonischen Simplifikator für Brüche. Dazu sei festgehalten, dass aus der Definition von ∼ für jedes t ∈ Z \ {0} sofort z t·z = n t·n folgt. Je nach dem, ob man diese Gleichheit von links nach rechts oder von rechts nach links liest, heißt das, dass man Brüche mit ganzen Zahlen im Zähler und Nenner erweitern oder gemeinsame Teiler im Zähler und Nenner kürzen kann. Als kanonischen Repräsentanten der Äquivalenzklasse nz wählen wir dann ein Paar hz ′ , n′ i so, dass z ′ und n′ relativ prim sind mit n′ > 0, d.h. als kanonischen Simplifikator wählen wir kanonischQ (hz, ni) := hQuotZ(z, t) · sgn(n), QuotZ(|n|, t)i wobei t = ggT(z, n). Auch hier wird man wieder den Konstruktor für die Datenstruktur so einrichten, dass in Objekten des Typs Q immer kanonische Repräsentanten vorliegen. Auf solchen Objekten definiert man nun die Grundoperationen hz, ni ± hz ′ , n′ i := kanonischQ (hzn′ ± z ′ n, nn′ i) hz, ni · hz ′ , n′ i := kanonischQ (hzz ′ , nn′ i) hz, ni−1 := hn, zi hz, ni/hz ′ , n′ i := hz, ni · hn′ , z ′ i. y 86 Kapitel II. Zahlbereiche Unter der Voraussetzung, dass die Operanden in kanonischer Form vorliegen, lassen sich für die Addition und Multiplikation effizientere Algorithmen angeben als die oben gezeigten Standardverfahren. Satz. Seien z, n, z ′, n′ ∈ Z mit ggT(z, n) = ggT(z ′ , n′ ) = 1. 1. Seien weiters t = ggT(n, n′ ), N = QuotZ(n, t) und N ′ = QuotZ(n′ , t), dann ist ggT(zN ′ ± z ′ N, nN ′ ) = ggT(zN ′ ± z ′ N, t). 2. Seien t = ggT(z, n′ ), t′ = ggT(z ′ , n) und Z = QuotZ(z, t), N = QuotZ(n, t′ ), Z ′ = QuotZ(z ′ , t′ ), N ′ = QuotZ(n′ , t), dann ist ggT(ZZ ′ , N N ′ ) = 1. Die nach ihrem Erfinder benannten Henrici19 -Algorithmen beruhen nun darauf, dass laut obigem Satz der ggT, der durch den kanonischen Simplifikator aus Zähler und Nenner des Endresultats zu eliminieren ist, durch mehrere ggTBerechnungen mit kleineren Eingabezahlen ermittelt werden kann. Im Fall der Addition ist ggT(n, n′ ) im Zähler und Nenner des Resultats jedenfalls ein gemeinsamer Faktor, den wir in einem ersten Schritt eliminieren. Teil 1 des Satzes sagt uns, wie wir alle restlichen gemeinsamen Faktoren einfacher finden können. Im Fall der Multiplikation sagt uns Teil 2 des Satzes, dass nach kreuzweisem Kürzen“ kei” ne weiteren gemeinsamen Faktoren mehr enthalten sein können. Bedingung aber ist jeweils, dass von kaninischen Brüchen als Input ausgegangen wird. Algorithmus AddQHenrici: Addition von Brüchen z ← zähler(a), n ← nenner(a) z ′ ← zähler(b), n′ ← nenner(b) t ← GGTZEuklid(n, n′ ) N ← QuotZ(n, t), N ′ ← QuotZ(n′ , t) z ← z · N ′ + z′N , n ← n · N ′ t ← GGTZ(z, t) zähler(s) ← QuotZ(z, t) nenner(s) ← Quot(n, t) return a Aufruf: Eingabe: Ausgabe: mit: AddQHenrici(a, b) a, b ∈ Q s∈Q s = a + b. Computerprogrammierung. In den Henrici-Algorithmen weisen wir das Endresultat direkt dem Nenner und Zähler des Resultats zu, um zu verdeutlichen, dass schon bekannt ist, dass es sich dabei um die kanonische Form handelt. Wir verwenden daher nicht den Konstruktor Q der Datenstruktur, da ansonsten unnötig versucht wird, den Bruch in kanonische Form zu bringen. 19 Henrici, Peter: ??? 87 8. Q Algorithmus MultQHenrici: Multiplikation von Brüchen z ← zähler(a), n ← nenner(a) z ′ ← zähler(b), n′ ← nenner(b) t ← GGTZEuklid(z, n′ ) t′ ← GGTZEuklid(z ′ , n) Z ← QuotZ(z, t), N ← QuotZ(n, t′ ) Z ′ ← QuotZ(z ′ , t′ ), N ′ ← QuotZ(n′ , t) zähler(p) ← Z · Z ′ nenner(p) ← N · N ′ return p Aufruf: Eingabe: Ausgabe: mit: MultQHenrici(a, b) a, b ∈ Q p∈Q p = a · b. Die Henrici Algorithmen bringen gegenüber den klassischen Algorithmen keine Verbesserung der asymptotischen Komplexität (gemessen in der Länge der Zahlen, die in die ggT-Berechnung eingehen), jedoch besitzen sie einen deutlich niedrigeren Proportionalitätsfaktor, was jedenfalls niedrigere Rechenzeiten zur Folge hat, siehe Details dazu in [Win96].