Rechner-Arithmetik

Werbung
Rechner-Arithmetik
Vorlesung im SS 2001/2002
Norbert Th. Müller
Abteilung Informatik
Universität Trier
Wenn man den Rechner zu seiner namensgebenden Aufgabe, dem Rechnen, einsetzen will, sollte man die Arbeitsweise und die Grenzen der einbauten Arithmetik kennen. Als Beispiel kann die folgende einfache Iteration dienen,
bei der nach wenigen Operationen das Resultat bereits vollkommen verfälscht ist:
xi+1 = 3.75 · xi · (1 − xi ),
x0 = 0.5
Bereits bei x100 sind alle mit herkömmlicher Arithmetik berechneten Stellen falsch:
x60
x70
x80
x90
x100
x110
exakte Arithmetik
+.7990863343...
+.4521952998...
+.8561779966...
+.7399137486...
+.8882939922...
+.7156795292...
Java double
0.79908633 70...
0.4521952 586...
0.85617 59906...
0.7 400517104...
0. 9017659679...
0. 2201217854...
In der Vorlesung werden die grundlegenden Algorithmen und Datenstrukturen besprochen, die für die Arithmetik
bei existierender Hardware und insbesondere bei Software-Lösungen eingesetzt werden, und mit deren Hilfe man
Effekte wie oben verstehen und oft auch vermeiden kann. Themen sind unter anderem:
Vom Bit zur Zahl — elementare Arithmetik — schnelle Multiplikation — rationale Arithmetik —
Fließkomma-Zahlen — IEEE 754/854 Fließkomma-Standard — MP-Pakete: GMP und MPFR — Reduktionsmethoden in der Arithmetik — Intervall-Arithmetik — Pi und das AGM — modulare und redundante Zahl-Darstellungen — exaktes Rechnen mit reellen Zahlen — Zahldarstellungen in HighlevelPaketen — symbolisches Rechnen — Rechnen mit arithmetischen Zahlen
Literatur
Inhaltsverzeichnis
Inhaltsverzeichnis
1 Einleitung
5
1.1
ganzzahlige Arithmetik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.2
Lineare Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.3
Grenzwerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.4
Iterierte Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.5
Die Ariane-5-Explosion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.6
Der Pentium-Bug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.7
Der Patriot-Scud-Vorfall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
2 Natürliche / ganze Zahlen
12
2.1
Notationen, natürliche Zahlen und Berechenbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . .
12
2.2
Ganze Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
2.3
Die Multiplikation natürlicher Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
2.4
Divisions-Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
2.5
Redundante Zahl-Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
2.6
Modulare Arithmetik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
2.7
Kompakte Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
3 Rechnen mit rationalen Zahlen
34
4 Fließkomma-Arithmetik
37
5 Intervall-Arithmetik
60
Literatur
[Av61] A. Avizienis, Signed-Digit Number Representations for Fast Parallel Arithmetic, IRE Transactions on Electronic Computers Vol. EC-10 (1961) 389-400 30
[Ba88] D.H. Bailey, The computation of π to 29,360,000 Decimal Digits Using Borweins Quartically Convergent Algorithm
Mathematics of Computation Vol. 50 Number 181 (1988) 238-296
[Bo87] J.M. Borwein & P.B. Borwein, Pi and the AGM, A study in analythic number theory, Wiley, New York, 1987
[BoCa90] H. Boehm & R. Cartwright, Exact Real Arithmetic: Formulating real numbers as functions. In T. D., editor, Research
Topics in Functional Programming, 43-64 (Addison-Wesley, 1990)
[BSS89] L. Blum & M. Shub & S. Smale, On a theory of computation and complexity over the real numbers: NP-completeness,
recursive functions and universal machines, Bulletin of the AMS 21, 1, July 1989
[Bt75] R.P. Brent, The complexity of multiple precision arithmetic, Proc. Seminar on Complexity of Computational Problem
Solving, Queensland U. Press, Brisbane, Australia (1975) 126-165
[Bt76] R.P. Brent, Fast multiple precision evaluation of elementary functions, J. ACM 23 (1976) 242-251
[Bt78] R.P. Brent, A Fortran multiple precision package, ACM Trans. Math. Software 4 (1978), pp 57-70
[Br96] V. Brattka, Recursive characterisation of computable real-valued functions and relations, Theoret. Comput. Sci. 162
(1996),47-77
2
Literatur
Literatur
[Br99] V. Brattka, Recursive and Computable Operations over Topological Structures, Thesis, Informatik Berichte 255 - 7/1999,
FernUniversität Hagen
[BrHe94] V. Brattka & P. Hertling, Continuity and Computability of Relations, Informatik Berichte 164 - 9/1994, FernUniversität Hagen,
[BrHe95] V. Brattka & P. Hertling, Feasible Real Random Access Machines, Informatik Berichte 193 - 12/1995, FernUniversität Hagen,
[Bro12] L. E. J. Brouwer, Über Abbildungen von Mannigfaltigkeiten. Math. Ann.,71:97-115, 1912 62
[CoAa89] Cook, S.A. und Aanderaa, S.O., On the minimum computation time of functions, Trans. Amer. Math. Soc. 142 (1969)
291-314
[EdPo97] A. Edalat & P. Potts, A new representation for exact real numbers, Proc. of Mathematical Foundations of Programming Semantics 13, Electronic notes in Theoretical Computer Science 6, Elsevier Science B.V., 1997, URL:
www.elsevier.nl/locate/entcs/volume6.html
[FiSt74] Fischer, M.J. und Stockmeyer, L.J. Fast on-line integer multiplication, J. Comput. System Scis. 9 (1974) 317-331
[Gr02] T. Granlund, GMP 4.0, http://www.swox.com/gmp/ 22
[GL00] P. Gowland, D. Lester, The Correctness of an Implementation of Exact Arithmetic, 4th Conference on Real Numbers
and Computers, 2000, Dagstuhl, 125-140
[HM00] S. Heinrich & E. Novak et al., The Inverse of the Star-Discrepancy depends linearly on the Dimension, Acta Arithmetica, to appear
[HS66] F. C. Hennie and R. E. Stearns, Two-tape simulation of multitape Turing machines Journal of the ACM, 13(4):533-546,
1966 17
[Ka96] W.
Kahan,
IEEE
Standard
754
for
BInary
www.cs.berkeley.edu/ wkahan/ieee754status/ieee754.ps, 1996
Floating
Point
Arithmetic,
available
at
[Ke96] R. B. Kearfott. Interval computations: Introduction, uses, and resources. Euromath Bulletin, 2(1):95-112, 1996. 60, 63
[Kl93] R. Klatte & U. Kulisch et al., C-XSC , a C++ Class Library for Extended Scientific Computing (Springer, Berlin 1993)
[Kn73] Knuth, Donald Ervin : The Art Of Computer Programming (Volume 1 / Fundamental Algorithms ). Second Edition.
Reading: Addison-Wesley, 1981. 35
[Kn81] Knuth, Donald Ervin : The Art Of Computer Programming (Volume 2 / Seminumerical Algorithms ). Second Edition.
Reading: Addison-Wesley, 1981. 12, 28, 29, 32
[Ko91] K. Ko, Complexity Theory of Real Functions, (Birkhäuser, Boston 1991)
[Ku96] U. Kulisch, Memorandum über Computer, Arithmetik und Numerik (Universität Karlsruhe, Institut für angewandte
Mathematik) 6, 8
[Mm96] V. Ménissier-Morain, Arbitrary precision real arithmetic: design and algorithms, J. Symbolic Computation, 1996, 11
7
[MJM89] Jean-Michel Muller, Arithmetique des Ordinateurs, Masson, Paris, 1989 6
[MJM97] Jean-Michel Muller, Elementary Functions, Birkhäuser, Boston, 1997 42
[Mo62] R. E. Moore, Interval Arithmetic and Automatic Error Analysis in Digital Computing, Ph.d. Dissertation, Department
of Mathematics, Stanford University, Stanford, California, Nov. 1962. Published as Applied Mathematics and Statistics
Laboratories Technical Report No. 25. 60
[Mu86] N.Th. Müller, Subpolynomial complexity classes of real functions and real numbers, Proc. 13th ICALP, Lecture notes
in computer science 226 (Springer, Berlin) pages 284-293 (1986) 30
[Mu87] N.Th. Müller, Uniform computational complexity of Taylor series, Proc. 14th ICALP, Lecture notes in computer
science 267 (Springer, Berlin) pages 435-444 (1987) 30
[Mu88] Müller, N.Th., Untersuchungen zur Komplexität reeller Funktionen, Dissertation (FernUniversität Hagen, 1988)
[Mu93] N.Th. Müller, Polynomial Time Computation of Taylor Series, Proc. 22 JAIIO - PANEL ’93, Part 2, Buenos Aires,
1993, 259-281
(also available at http://www.informatik.uni-trier.de/˜mueller)
3
Literatur
Literatur
[Mu96] Müller, N.Th., Towards a real Real RAM: a Prototype using C++, (preliminary version), Second Workshop on Constructivity and Complexity in Analysis, Forschungsbericht Mathematik-Informatik, Universität Trier 96-44, Seiten 59-66
(1996) (also available at http://www.informatik.uni-trier.de/˜mueller)
[Mu97] N.Th. Müller, Towards a real RealRAM: a Prototype using C++, Proc. 6th International Conference on Numerical
Analysis, Plovdiv, 1997
[Mu98] N.Th. Müller, Implementing limits in an interactive RealRAM, 3rd Conference on Real Numbers and Computers, 1998,
Paris, 13-26
[Mu01] N.Th. Müller, The iRRAM: Exact Arithmetic in C++, Computability and Complexity in Analysis - CCA2000, Lecture
notes in computer science 2064 (Springer, Berlin, 2001) 222-252 35, 42
[1] he iRRAM: Exact Arithmetic in C++, Workshop on Constructivity and Complexity in Analysis Swansea, Seiten .... (2000)
[Mu02] N.Th. Müller, Real Numbers and BDDs, submitted 31
[Pa00] B. Parhami, Computer Arithmetic: Algorithms and Hardware Designs, Oxford University Press, New York, 2000 22,
26, 30, 37
[PFM74] Patteron, Fischer, Meyer, An Improved Overlap Argument for On-Line Multiplication SIAM-AMS Proceedings Vol
7, 97-111, 1974 28
[Rio94] M. Riordan, ftp://ripem.msu.edu/pub/bignum/BIGNUMS.TXT
[Sch80] A. Schönhage, Storage Modification Machines, SIAM J. on Computing 9(3):490-508, 1980 28
[Sch90] A. Schönhage, Numerik analytischer Funktionen und Komplexität, Jber. d. Dt. Math.-Verein. 92 (1990) 1-20
[TZ99] J.V. Tucker, J.I. Zucker, Computation by ‘While’ programs on topological partial algebras, Theoretical Computer
Science 219 (1999) 379-420
[We87] K. Weihrauch, Computability (volume 9 of: EATCS Monographs on Theoretical Computer Science), (Springer, Berlin,
1987) 12
[We95] K. Weihrauch, A Simple Introduction to Computable Analysis, Informatik Berichte 171 - 2/1995, FernUniversität
Hagen
[We97] Weihrauch, K., A Foundation for Computable Analysis, Proc. DMTCS´96, (Springer, Singapore, 1997) 66-89
[We00] K. Weihrauch, Computable Analysis. An Introduction, Springer, Berlin 2000 32, 42
[Zi00] P. Zimmermann, MPFR: A Library for Multiprecision Floating-Point Arithmetic with Exact Rounding, 4th Conference
on Real Numbers and Computers, 2000, Dagstuhl, 89-90, see also http://www.loria.fr/projets/mpfr/ 41
4
1 EINLEITUNG
1
Einleitung
Stichworte: Integer-Überlauf, Beispiele von Kulisch und Jean-Michel Muller... Ariane-5, Patriot-Scud, PentiumBug
Wenn man mit dem Rechner rechnet, so treten gelegentlich unerwartete Effekte auf, die zeigen, dass es wichtig ist,
sich der Möglichkeiten und Grenzen der Arithmetik von Computern bewusst zu werden. In dieser Vorlesung werden
wir Hardware- und Software-Lösungen zur Arithmetik auf verschieden Zahlmengen untersuchen, wobei sowohl
praktische als auch theoretische Aspekte Beachtung finden.
In der englischen Sprache bedeuted hat das Wort arithmetic ebenso wie das Wort compute auch die einfache Bedeutung Rechnen, frei übersetzt heißt der Titel Rechnerarithmetik dieser Vorlesung also Rechner-Rechnen. Viele heutige
Anwendungen von Rechner rechnen kaum noch, sondern sie werten nur noch aus, was einer weiteren Bedeutung des
Wortes Computer (der Auswerter) eigentlich näher kommt.
Im Internet findet man eine Bibliography zum Thema Computer Arithmetic unter liinwww.ira.uka.de/bibliography/Theory/arith.
(von Peter Kornerup, eher praxisorientiert, mit z.Zt. ca. 600 Artikeln) unv zum Thema Constructivity, Computability
and Complexity in Analysis (eher theorieorientiert, Vasco Brattka et.al. ca 500 Artikel) unter www.informatik.fernuni-hagen.de/i
Weitere themen-relevante Links sind:
• www.cs.wisc.edu/ arch/www/
• www.cs.wisc.edu/ arch/sigarch/SIGARCH.html ACM Special Interest Group on Computer Architecture, eher
praxisorientiert
Als Motivation werden wir zunächst einige eher theoretische Beispiele für problembehaftete Berechnungen betrachten, bevor wir anhand dreier realer Katastophen kurz die möglichen Auswirkungen des falschen Umgangs mit Zahlen
beleuchten.
Beispiele für Arithmetik-bezogene Software-Fehler finden sich im Internet unter www5.in.tum.de/persons/huckle/bugse.html
Wir werden hier drei dieser Fehler ansprechen: die Ariane-5-Explosion (4. Juni 1996), der Patriot-Scud-Vorfall (25.
Februar 1991) und den Pentium-Bug (1994/1995).
1.1
ganzzahlige Arithmetik
Wir betrachten folgendes kleine C++-Programm ganze_zahlen.cc:
#include <iostream.h>
int main (){
long x=3, i;
for ( i=1;i<=10;i++ ) {
x=x*x;
cout << x << endl;
}
}
Übersetzt mit g++ -Wall -g -o ganze_zahlen ganze_zahlen.cc, entstehen als Ausgabe hier die folgenden eher zufällig aussehenden Zahlen:
9
81
6561
43046721
-501334399
2038349057
5
1 EINLEITUNG
1.2 Lineare Algebra
-1970898431
120648705
1995565057
-1876701183
Eine allgemeine Erklärung dieses Verhaltens finden wir in Kapitel über natürliche Zahlen.
1.2
Lineare Algebra
In [Ku96] findet man folgendes Beispiel: Betrachte das einfache lineare Gleichungssystem A ∗ x = b mit
1
64919121 −159018721
und b = (bi ) =
A = (aij ) =
0
41869520.5 −102558961
Als Lösungsformel ergibt sich
x1 = a22 /(a11 · a22 − a12 · a21 )
x2 = −a21 /(a11 · a22 − a12 · a21 )
Mit double precision Variablen erhält man für die Lösung:
x̃1 = 102558961, x̃2 = 41869520.5
Die exakten Werte sind jedoch:
x1 = 205117922, x2 = 83739041
Vier einfache Rechenoperationen führen also bereits zu komplett falschen Resultaten!
Ähnliche Probleme gibt es bei computergestützer Geometrie, wo es beispielsweise sehr schwer sein kann, festzustellen, ob ein Punkt der Ebene im Innern eines Polygones liegt oder nicht.
1.3
Grenzwerte
Das folgende Beispiel stammt aus [MJM89]: Betrachte die Folge
a0 =
11
61
1130 − 3000/an−1
, a1 = , an+1 = 111 −
2
11
an
Wogegen konvergiert diese Folge?
Das folgende kleine Programm liefert eine Lösung (?)
#include <iostream.h>
int main (){
float a=11.0/2.0, b=61.0/11.0, c;
for (long i=1;i<=20;i++ ) {
cout << a << endl;
c=111-(1130-3000/a)/b;
a=b; b=c;
}
}
6
1 EINLEITUNG
1.3 Grenzwerte
Als Resultat ergibt sich (gerundet auf eine Nachkommastelle):
i
float
double
exakt
i
float
double
exakt
1
5.5
5.5
5.5
11
100.1
5.9
5.9
2
5.6
5.6
5.6
12
3
5.6
5.6
5.6
13
4
5.7
5.7
5.7
14
5
.5.7
5.7
5.7
15
5.9
5.9
6.5
5.9
15.4
5.9
67.5
5.9
6
7
5.7
4.6
5.7
5.8
5.7
5.8
16
17
100.0
97.1 99.8
5.9
8
−20.5
5.8
5.8
18
9
134.1
5.8
5.8
19
10
101.5
5.9
5.9
20
100.0
6.0
Die exakten Werte können dabei mit rationaler oder exakter reeller Arithmetik bestimmt werden. Die Berechnungen
liefern also ziemlich schnell komplett falsche Werte. Man kann leicht zeigen, dass der exakte Wert der Iteration bei
6n+1 + 5n+1
6n + 5 n
an =
liegt, also lim an = 6.
Eine Erklärung des Phänomens ist , dass die Funktion
f (x) := 111 −
1130 −
x
3000
x
drei Fixpunkte besitzt: 5,6 und 100, wobei 100 ein anziehender Fixpunkt ist, während 5 und insbesondere 6 abweisende Fixpunkte sind (vgl.z.B. [Mm96]). Die folgenden Graphiken zeigen den Funktionsverlauf von f allgemein
und in Nähe der Fixpunkte:
120
111-(1130-3000/x)/x
111-(1130-3000/x)/x
5.4
100
5.2
80
60
5
40
4.8
20
4.6
0
20
40
60
80
100
4.6
4.8
5
111-(1130-3000/x)/x
100.4
6.2
100.2
6
100
5.8
99.8
5.6
99.6
5.8
6
6.2
5.4
111-(1130-3000/x)/x
6.4
5.6
5.2
6.4
99.6
7
99.8
100
100.2
100.4
1 EINLEITUNG
1.4
1.4 Iterierte Funktionen
Iterierte Funktionen
Sehr hartnäckig ist die folgende einfache Iteration, bei der nach wenigen Operationen das Resultat bereits vollkommen verfälscht ist. Auch sie findet sich z.B. in [Ku96] (logistische Gleichung, chaotisches System ohne Konvergenz):
xi+1 = 3.75 · xi · (1 − xi ),
x0 = 0.5
Bereits bei x100 sind alle mit herkömmlicher Arithmetik berechneten Stellen falsch:
x60
x70
x80
x90
x100
x110
exakte Arithmetik
+.7990863343...
+.4521952998...
+.8561779966...
+.7399137486...
+.8882939922...
+.7156795292...
Java double
0.79908633 70...
0.4521952 586...
0.85617 59906...
0.7 400517104...
0. 9017659679...
0. 2201217854...
Während bei der Grenzweriteration im vorigen Abschnitt rationale Arithmetik verwendet werden konnte, um richtige
Werte zu finden, versagt sogar diese hier. Im Abschnitt über exakte Arithmetik werden wir untersuchen, wie die
Vergleichswerte bestimmt wurden.
1.5
Die Ariane-5-Explosion
Am 4. Juni 1996 endete der Jungfernflug der Ariane-5 bereits nach ca. 40 Sekunden:
Der offizielle Report der ESA zu dieser milliardenteuren Panne findet sich unter www.esa.int/export/esaCP/Pr_33_1996_p_EN.h
Ein kurzer Film zur Explosion findet sich bei CNN: www.cnn.com/WORLD/9606/04/rocket.explode/ariane.mov .
Grund der Explosion war folgender: Ein Leitsystem (mit Gyroskopen und Beschleunigungsmessern) versuchte, die
Horizontalgeschwindigkeit aus einer 64bit Variablen in 16 Bit zu speichern, machte wegen des entstehenden Überlaufes eine Fehlermeldung und schaltete sich ab. Redundante Auslegung dieses Systems half nicht: Das zweite Leitsystem benutzte die gleiche Software.
Der On-Board-Computer der Ariane interpretierte die Fehlermeldungen als Flugdaten und versuchte daraufhin eine
abrupte Kurskorrektur. Als die Booster-Raketen abzureissen drohten, wurde die Rakete sicherheitshalber gesprengt.
Bei den Ariane-4-Raketen hatte die selbe Zuweisung keine Probleme gemacht, aber die Ariane-5 war schneller...
Weitere Absurdität: Die Bestimmung der Horizontalgeschwindigkeit wäre nach dem Start gar nicht mehr notwendig
gewesen...
1.6
Der Pentium-Bug
Die ersten Pentium-Chips von Intel (von 1994) hatten einen Fehler in der Divisionsroutine, aufgedeckt von Prof.
Thomas Nicely, Lynchburg College. Kosten: ca. 400Mio $
8
1 EINLEITUNG
1.6 Der Pentium-Bug
Aus www.mackido.com/History/History_PentBug.html :
In mid ’94 Intel (and independant parties) found a floating point bug in their processor. While it is not
unusual for Hardware bugs in processors it is rare that they are this serious, furthermore Intel went out
of their way to hide the bug. It was about 6 months later that Intel finally went public with the bug (1).
Even then Intel did not valuntarily go public with the bug, it was because other people were discovering
it and a thread started on the internet.
Dr. Thomas Nicely was the first guy to publicly discover the bug, and he contacted Intel Tech Support
regarding this bug on Monday 24 October (call reference number 51270). But he was soon silenced by
Intel with an NDA as Intel tried to bury the problem. (2) The bug was found on many different pentiums
including the 60, 66, 75 and 90 mhz pentium chips, and effected millions of users.
Erläuterung von Intel zu dem Problem: support.intel.com/support/processors/pentium/fdiv/wp/
www.eee.bham.ac.uk/dsvp_gr/roxby/ee4a3/Lecture2/sld013.htm
A software bug encoded in hardware (Halfhill, 1995)
Divide algorithm uses a look up table with 1066 entries, but when downloading the coefficients to the
PLA section, only 1061 were loaded due to an error in a for loop! Committed to silicon and never
checked...
Aus www.ku.edu/cwis/units/IPPBR/pentium_fdiv/pentgrph.html :
Intel Pentium chips manufactured before a certain date have a bug in their floating point processor
which returns less than full precision results for some combinations of divisor and dividend when performing a Floating point DIVision (FDIV). A well known operation which exhibits this phenomenon is
4195835/3145727. You can check to see if your Pentium has the FDIV bug by entering the following
formula in the Windows calculator:
(4195835/3145727) ∗ 3145727 − 4195835
The image below is a 3d graph of the function x/y in the region of 4195835/3145727. Specifically, the
region is:
4195833.0 <= x <= 4195836.4
3145725.7 <= y <= 3145728.4
On a 486/66, the function graphs as a monotonically increasing surface with the low point in the foreground corner (where x is low and y is high) and a high point in the background corner (where x is
high and y is low). On a Pentium with the FDIV bug there are two triangular areas in the region where
an incorrect result is returned. The correct values all would round to 1.3338 and the incorrect values all
would round to 1.3337, an error in the 5th significant digit.
9
1 EINLEITUNG
1.7 Der Patriot-Scud-Vorfall
An archive of principal papers on the Pentium FDIV bug is available at: www.mathworks.com/company/pentium/index.sht
pentgrph
Spott:
Q: Why didn’t Intel call the Pentium the 586?
A: Because they added 486 and 100 on the first Pentium and got 585.999983605.
Eine Untersuchung der Ursachen des Fehler findet sich z.B. in citeseer.nj.nec.com/pratt95anatomy.html Weitere
Links:
www.maa.org/mathland/mathland_5_12.html
www.mathworks.com/company/pentium/index.shtml
1.7
Der Patriot-Scud-Vorfall
Am 25.2.1991, während des Golfkrieges, versagte eine Amerikanische Patriot-Abwehrbatterie bei einer heranfliegenden Irakischen Scud-Rakete. Diese traf ein Lager, tötete 28 Soldaten und verletzte etwa 100 weitere.
Weitere Links dazu:
10
1 EINLEITUNG
1.7 Der Patriot-Scud-Vorfall
www.ima.umn.edu/ arnold/disasters/patriot.html
www.fas.org/spp/starwars/gao/im92026.htm
Als Grund stellte sich heraus, dass die Umrechnung der Uhrzeit zu ungenau war: Die Systemuhr (Hardware mit
einer Auflösung von 0.1 s) der Abschussanlage wurde in einer Routine mit 0.1 multipliziert (dargestellt als 24-BitFixpunktzahl), um die Zeit in Sekunden zu berechnen. Wegen der ungenauen internen Darstellung der Zahl 0.1 war
nach 100 Stunden Betriebszeit ein Fehler von ca. 0.34 Sekunden entstanden. Da eine Scud mit 1,676 m/s fliegt,
konnte sie sich in dieser Zeit mehr als 500 Meter bewegen und war daher nicht mehr erreichbar.
Da folgende einfache C++-Programm zeigt den gleichen Effekt auf. Wegen anderer Hardware ist der Fehler hier
allerdings etwas kleiner, nämlich ‘nur’ 0.00536442:
#include <iostream.h>
int main (){
float tenth = 1.0 / 10.0;
long dauer= 1000*3600;
cout << "Fehler: " << dauer * tenth - dauer/10 << endl;
}
11
2 NATÜRLICHE / GANZE ZAHLEN
2
Rechnen mit natürlichen und ganzen Zahlen
Stichworte: Berechenbarkeitsmodelle, Zweier-Komplement-Darstellung, modulare Arithmetik, elementare Algorithmen, schnelle Multiplikation, GMP, redundante Zahl-Darstellungen, signed-digits,...
2.1
Notationen, natürliche Zahlen und Berechenbarkeit
Reale Rechner basieren (auf unterster Ebene) auf binärer Logik, d.h. sie verarbeiten Worte über dem Alphabet IB =
{0, 1}. Wenn wir nun mit anderen Daten, insbesondere mit Zahlmengen wie , , , , . . . rechnen möchten, müssen
wir die darzustellenden Mengen geeignet codieren. Daher verschlüsselt man üblicherweise die Elemente der Mengen,
auf denen man rechnen möchte. Wir werden hier die Begriffe in der Form verwenden, wie sie üblicherweise in der
Rekursionstheorie verwendet werden, vgl. z.B. [We87]. Basis ist dabei immer die Menge Σ∗ aller Worte über einem
endlichen Alphabet Σ. Σk sei die Menge der Worte der Länge k, λ sei das leere Wort.
Viele der in diesem Abschnitt angesprochenen praxis-orientierten Algorithmen werden auch im Standardwerk [Kn81]
von Knuth behandelt.
2.1 Definition. Eine Notation ν einer Menge X ist eine (evtl. partielle) surjektive Abbildung
ν : Σ∗ X
2
Anmerkungen: (i) Ist ν eine Notation von X, so gibt es für jedes x ∈ X (mindestens) ein w ∈ Σ ∗ mit ν(w) = x.
Dann wird w als ‘Name’ von x bezeichnet. Jedes x ∈ X hat also einen oder mehrere Namen, aber nicht jedes w ∈ Σ ∗
muß Name eines x ∈ X sein.
(ii) Da Σ∗ abzählbar ist und Notationen surjektiv sind, muß jede ‘notierbare’ Menge X ebenfalls abzählbar sein.
Insbesondere sind damit die reellen Zahlen nicht ‘notierbar’.
(iii) Meist werden wir als Alphabet IB verwenden. Bereits die Betrachtung der gesamten Menge IB ∗ ist eine Abstraktion, da reale Rechner ja nur einen endlichen Speicher besitzen. Zudem ist die Hardware des Rechners auf feste
Wortlängen festgelegt, etwa IBi mit i ∈ {8, 16, 32, 64}.
Bei natürlichen Zahlen ist folgende binäre (totale) Notation (·) 2 naheliegend, bei der wir die Bits 0 und 1 mit den
natürlichen Zahlen 0 und 1 identifizieren:
2.2 Definition. Die Binär-Notation (·) 2 : IB∗ →
sei definiert durch:
(bn . . . b0 )2 :=
n
X
i=0
bi · 2 i
sowie (λ)2 := 0 .
2
Damit sind zum Beispiel 101 und 0101 beides „Namen“ der natürlichen Zahl 5 in Binärnotation.
Anstelle der Zahl 2 kann man jede beliebige natürliche Zahl r ≥ 2 als Basis der Notation verwenden:
2.3 Definition. Es sei Σr := {0, 1, . . . , r−1}. Die r-adische Notation (·) r : Σ∗r →
(dn . . . d0 )r :=
n
X
i=0
sowie (λ)r := 0 .
sei definiert durch:
di · r i
2
12
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Um zu zeigen, dass jedes (·)r surjektiv ist, kann man induktiv die folgende Eigenschaft nachprüfen:
2.4 Lemma. Für alle r und k ∈
gilt:
{(w)r | w ∈ Σkr } = {0, . . . r k − 1}
2
(·)2 ist auch die Form, in der natürliche Zahlen in der Rechnerhardware dargestellt werden. Da hier die Wortlänge
fest ist, kann man nur zunächst einmal nur endliche Bereiche notieren.
Beispiele:
typedef unsigned int u_int32_t mit 32 Bit, Werte-Bereich {0, . . . , 4294967295}
typedef unsigned short u_int16_t mit 16 Bit, Werte-Bereich {0, . . . , 65535}.
Damit haben wir viele verschiedene Möglichkeiten, natürliche Zahlen zu notieren. Im Wesentlichen sind diese jedoch
gleichwertig. Da dies bei den später definierten Darstellungen reeller Zahlen nicht der Fall sein wird, führen wir hier
schon die Begriffe ein, die in analoger Form bei wichtig sein werden. Zunächst definieren wir Turing-Maschinen
(nach Alan M. Turing, 1936) und die dadurch implizierten Begriffe von Berechenbarkeit und Komplexität. Wohl
allgemein akzeptiert ist die Church’sche These (erstmals 1935 formuliert, wenn auch für ein anderes BerechnungsModell):
Die mit Turing–Maschinen berechenbaren Funktionen sind genau die im intuitiven Sinne „berechenbaren“ Funktionen (auf Worten).
Eine einheitliche Definition dieser Turingmaschinen existiert nicht, aber die wesentlichen Komponenten sind stets
ähnlich. Ferner sind viele der mit Turingmaschinen erzielten Resultate von den Feinheiten der Definitionen unabhängig. Dennoch ist eine exakte Angabe der hier verwendeten Definition sinnvoll, um später Beweisideen ausformulieren
zu können.
Eine Turingmaschine wird bei uns wie folgt arbeiten: Mit Hilfe einer endlichen ‘Kontrolleinheit’ kann sie verschiedene ‘Bänder’ bearbeiten: (a) ein endliches Band, auf dem die Maschine ihre Eingabe erhält, (b) ein einseitig unendliches Band, auf das die Ausgabe geschrieben wird und (c) eine feste Anzahl beidseitig unendlicher Arbeitsbänder,
auf denen Daten gespeichert werden können.
Jedes dieser Bänder ist in Zellen unterteilt, in denen jeweils ein Zeichen eines vorgebenen endlichen Alphabetes
gespeichert werden kann. Zu jedem Band existiert genau ein beweglicher ‘Kopf’, der auf einer der Zellen des Band
positioniert ist. Die Turingmaschine kann in einem Schritt genau ein Zeichen eines Bandes verarbeiten (also das
Zeichen unter dem Kopf des Bandes lesen oder überschreiben) oder aber den Kopf eines Bandes um eine Zelle
verschieben.
Damit die Turingmaschine die Möglichkeit hat, Anfang und Ende des (endlichen) Eingabebandes zu erkennen, ist
dieses mit Bandendemarkierungen versehen. Graphisch können wir eine Turingmaschine mit zwei Arbeitsbändern
also wie folgt darstellen:
13
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
#
endliche
Kontrolle
$
Eingabeband
(nur
lesen)
6
...
...
6 Arbeitsband (lesen/schreiben)
...
...
6 Arbeitsband (lesen/schreiben)
Ausgabeband (nur schreiben)
...
6
2.5 Definition. Eine Turingmaschine M mit k Arbeitsbändern ist definiert als ein 7-Tupel (Z, Σ 1 , Σ2 , ∆, δ, z0 , ze )
mit den folgenden Komponenten:
• Z endliche ‘Zustandsmenge’,
• Σ1 endliches Eingabealphabet, Σ2 endliches Ausgabealphabet,
• ∆ endliches ‘Bandalphabet’ mit einem ‘Leer’-Zeichen (‘Blank’) B ∈ ∆,
• z0 ∈ Z ‘Anfangszustand’,
• ze ∈ Z ‘Endzustand’,
• δ ‘Übergangsfunktion’.
δ ist dabei eine Abbildung von Z \ {ze } in die Menge der bei der Turingmaschine möglichen Operationen, die wir
erst später angeben werden.
Zur Beschreibung der Auswirkungen dieser Operationen auf die Bänder und die Kontrolleinheit der Turingmaschine
verwenden wir ‘Konfigurationen’. Den Inhalt eines Bandes werden dabei in einem Wort u↑av festhalten, wobei der
Pfeil ↑ vor dem Zeichen a steht, auf dem der Kopf des Bandes positioniert ist. Entsprechend gibt das Wort u den
Inhalt des Bandes links vom Kopf und v den Inhalt des Bandes rechts vom Kopf an. Vom Ausgabeband, das ja nur
beschrieben werden darf, brauchen wir uns nur den bereits beschriebenen linken Teil u zu merken. Die Menge K der
Konfigurationen der Turingmaschine M wird also definiert als die Menge
∗
+
K := Z × Σ∗2 × Σ1 ◦{↑}◦Σ1 × (∆∗ ◦{↑}◦∆+ )k
Dabei sei Σ1 := Σ1 ∪ {#, $} das um die ‘Bandendemarker’ # und $ erweiterte Eingabealphabet. Bei einer solchen
Konfiguration
κ = (z, ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , uk ↑ak vk )
wird ua als Inhalt des Ausgabebandes bezeichnet, u e ↑ae ve als Inhalt des Eingabebandes und ui ↑ai vi als Inhalt des
Arbeitsbandes i; z ist der Zustand der Konfiguration κ.
Zu einer ‘Eingabe’ w ∈ Σ∗1 definieren wir eine ‘initiale’ oder ‘Anfangskonfiguration’ I M (w) durch:
IM (w) := (z0 , λ, ↑#w$, ↑B, . . . , ↑B)
14
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Hier steht der Kopf des Eingabebandes also auf dem linken Bandendemarker. Die Menge K e der Endkonfigurationen
ist definiert als
Ke := {κ ∈ K | der Zustand von κ ist der Endzustand z e }
: K K beschrieben. Mit ihr werden
Ein Schritt der Turingmaschine wird dann über die Einzelschritt-Funktion −→
M
die Auswirkungen aller bei der Turingmaschine möglichen Operationen beschrieben: Es sei
κ = (z, ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , uk ↑ak vk )
eine Konfiguration. Dann gilt κ−→
κ0 , falls einer der folgenden Fälle eintritt (abhängig von der beim Zustand z von
M
κ auszuführenden Operation):
1. (Schreiben auf das Ausgabeband)
Ist δ(z) = (A, x, z 0 ) (mit x ∈ Σ2 , z 0 ∈ Z), so ist
κ0 = (z 0 , ua ◦ x, ue ↑ae ve , u1 ↑a1 v1 , . . . , uk ↑ak vk )
2. (Testen auf Eingabeband und Verzweigen)
Ist δ(z) = (E, x, z1 , z2 ) (mit x ∈ Σ1 ∪ {#, $}, z1 , z2 ∈ Z), so ist
κ0 = (z 0 , ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , uk ↑ak vk )
mit
0
z :=
z1 ,
z2 ,
falls x = ae
falls x 6= ae
3. (Kopf auf Eingabeband nach rechts)
Ist δ(z) = (E, R, z 0 ) (mit z 0 ∈ Z), so ist
κ0 = (z 0 , ua , w0 , u1 ↑a1 v1 , . . . , uk ↑ak vk )
mit
w0 =
ue ae ↑ve
ue ↑ae ve ,
falls ve =
6 λ
falls ve = λ
4. (Kopf auf Eingabeband nach links)
Ist δ(z) = (E, L, z 0 ) (mit z 0 ∈ Z), so ist
κ0 = (z 0 , ua , w0 , u1 ↑a1 v1 , . . . , uk ↑ak vk )
mit
0
w =
u↑xae ve
ue ↑ae ve ,
falls lg(ue ) ≥ 1, d.h. ue = ux mit x ∈ Σ1
falls ue = λ
5. (Schreiben auf Arbeitsband i)
Ist δ(z) = (i, x, z 0 ) (mit 1 ≤ i ≤ k, x ∈ ∆, z 0 ∈ Z), so ist
κ0 = (z 0 , ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , ui ↑xvi , . . . , uk ↑ak vk )
6. (Testen auf Arbeitsband i und Verzweigen)
Ist δ(z) = (i, x, z1 , z2 ) (mit 1 ≤ i ≤ k, x ∈ ∆, z1 , z2 ∈ Z), so ist
κ0 = (z 0 , ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , uk ↑ak vk )
mit
0
z :=
z1 ,
z2 ,
15
falls x = ai
falls x 6= ai
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
7. (Kopf auf Arbeitsband i nach rechts)
Ist δ(z) = (i, R, z 0 ) (mit 1 ≤ i ≤ k, z 0 ∈ Z), so ist
κ0 = (z 0 , ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , w0 , . . . , uk ↑ak vk )
mit
0
w =
ui ai ↑vi
ui ai ↑B,
falls vi =
6 λ
falls vi = λ
8. (Kopf auf Arbeitsband i nach links)
Ist δ(z) = (i, L, z 0 ) (mit 1 ≤ i ≤ k, z 0 ∈ Z), so ist
κ0 = (z 0 , ua , ue ↑ae ve , u1 ↑a1 v1 , . . . , w0 , . . . , uk ↑ak vk )
mit
0
w =
u↑xai vi
↑Bai vi ,
falls lg(ui ) ≥ 1, d.h. ui = ua mit x ∈ ∆
falls ui = λ
2
Auf dem Eingabeband können die Bandendemarkierungen # und $ also erreicht und getestet, aber nicht überschritten
werden. Ferner kann die Eingabe nicht verändert werden. Das Ausgabeband kann nur beschrieben, aber nicht wieder
gelesen werden. Geschriebene Zeichen werden jeweils rechts an die bereits produzierte Ausgabe angefügt, eine
explizite Bewegung des Kopfes des Ausgabebandes brauchen wir daher nicht. Bei der (endlichen) Darstellung der
Arbeitsbänder werden bei Bedarf Blanks hinzugefügt, so daß die Maschine hier kein Bandende erreichen kann.
Damit scheinen für die Maschine diese Bänder unendlich lang zu sein.
Die oben definierte initiale Konfiguration stellt damit eine leeres Ausgabeband, ein Eingabeband, bei dem der Kopf
auf dem linken Bandende steht, und Arbeitsbänder, auf denen (für die Maschine) nur Blanks stehen, dar. Da δ für
den Endzustand nicht definiert ist, ist die Einzelschrittfunktion auf Endkonfigurationen nicht anwendbar.
n
Ausgehend von −→
wird die n-fache Iteration −→
: K K der Einzelschrittfunktion wie bei Funktionen üblich
M
M
n
n
0
κ für 1≤i≤n sowie
definiert: Es gilt κ−→
κ
,
wenn
Konfigurationen
κ
,
0 . . . , κn existieren mit κ0 = κ, κi−1 −→
M i
M
0
0
κn = κ . Eine n-fache Iteration entspricht also n Schritten der Maschine M . Insbesondere gilt κ −→
κ0 genau dann,
M
∗
0
wenn κ = κ gilt. Schließlich sei −→
⊆ κ × κ die transitive und reflexive Hülle der Einzelschrittfunktion, d.h.
M
∗
n
0 ⇐⇒ (∃n)κ−→
0.
κ−→
κ
κ
M
M
Da δ für Endzustände nicht definiert ist, gibt es bei Turingmaschinen zu jeder Anfangskonfiguration I M (w) maximal
∗
eine (aber gelegentlich auch keine einzige) Endkonfiguration κ mit I M (w)−→
κ. Im folgenden wird also wohldefiM
niert einer Turingmaschine die von ihr berechnete Funktion als Semantik zugewiesen.
2.6 Definition. Die Funktion fM : Σ∗1 Σ∗2 , die von der Turingmaschine M berechnet wird, ist definiert durch: Es
gilt fM (w) = v genau dann, wenn eine Endkonfiguration κ existiert, die als Inhalt des Ausgabebandes das Wort v
∗
hat und für die IM (w)−→
κ gilt. Existiert keine solche Endkonfiguration, so ist f M (w) undefiniert.
2
M
2.7 Definition. Die (Zeit-)Komplexität T M : Σ∗1 einer Turingmaschine M ist definiert durch
n
n
(∃κ ∈ Ke )IM (w)−→
κ
M
TM (w) :=
undefiniert sonst
Wir sagen: M arbeitet in Zeit O(t) (für t : → ) auf einer Menge G ⊆ Σ ∗1 , wenn es eine Konstante c ∈ gibt, so
daß TM (w) ≤ c · t(lg(w)) + c für alle w ∈ G gilt. M arbeitet in polynomialer (bzw. linearer) Zeit, wenn M in Zeit
O(nk ) für ein k ∈ (bzw. O(n)) arbeitet. Entsprechend sagen wir, daß eine Funktion f in Zeit O(t) (polynomialer
Zeit/linearer Zeit) auf G berechenbar ist, wenn eine Maschine M mit f = f M existiert, die in entsprechender Zeit
auf G arbeitet.
2
16
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Anmerkung: Später wird meist Def(f ) 6= Σ ∗1 sein, so daß TM (w) nicht auf ganz Σ∗1 definiert ist. Die Komplexität einer Maschine M wird uns zudem meist nur für spezielle Eingaben interessieren (also auf einer Menge
G ⊂
Def(fM )).
6=
Bei der Definition der Turingmaschinen hatten wir Arbeitsbänder in beliebiger Anzahl und ein beliebig großes Arbeitsalphabet zugelassen. Beide Komponenten können reduziert werden (ohne Beweis):
2.8 Lemma. Reduktion des Arbeitsalphabetes:
Zu jeder Turingmaschine M gibt es eine Turingmaschine M 0 mit der gleichen Zahl von Arbeitsbändern und dem
Arbeitsalphabet Σ = {B, 1}, so daß fM = fM 0 und TM 0 (w) ≤ c · TM (w) für alle w ∈ Def(fM ) und eine Konstante
c gilt.
Reduktion der Zahl der Arbeitsbänder:
Zu jeder Turingmaschine M mit k Arbeitsbändern gibt es eine Turingmaschine M 0 mit nur einem Arbeitsband, so
daß fM = fM 0 und TM 0 (w) ≤ c · TM (w)2 für alle w ∈ Def(fM ) und eine Konstante c gilt.
2
Anmerkungen: (i) Stehen zwei Arbeitsbänder zur Verfügung, so kann die obige Zeitschranke verbessert werden: Zur
Simulation einer beliebigen festen Zahl von Arbeitsbändern mit zwei Arbeitsbändern reichen c·T M (w)·log TM (w)+
c Schritte aus [HS66].
(ii) Für Aussagen zur Fragen der Berechenbarkeit sind die Zahl der Arbeitsbänder und die Größe des Arbeitsalphabetes damit offensichtlich nicht relevant.
(iii) Wegen der nur linearen Änderung der Komplexität bei der Reduktion des Arbeitsalphabetes können wir dessen
Größe in Zukunft ignorieren. Nur ein oder zwei Arbeitsbänder zu benutzen, könnte die Komplexität der in folgenden
betrachteten Algorithmen jedoch signifikant verändern. Die Komplexitätaussagen werden also im folgenden stets für
Turingmaschinen mit fester, aber beliebig hoher Zahl von Bändern formuliert werden, selbst wenn dies nicht jedes
Mal ausdrücklich gesagt wird.
2.9 Definition. Eine Funktion f : Σ∗1 Σ∗2 heißt berechenbar (oder partiell–rekursiv), wenn es eine Turingmaschine
M mit f = fM gibt. Es sei
PR := {f : Σ∗1 Σ∗2 | f ist berechenbar }
2
Anmerkung: Die Abkürzung PR ergibt sich aus partiell–rekursiv: Da es Turingmaschinen M gibt, die nicht bei
jeder Eingabe ihre Berechnung beenden, ist f M nicht unbedingt eine totale Funktion. Die Beziechnung ‘rekursiv’
kommt von einer ersten Definitionen von Berechenbarkeit mit einem rekursiven Definitionsmechanismus (Kleene,
1937, ‘µ–rekursive’ Funktionen).
2.10 Satz. Für alle berechenbaren Funktionen f, g ∈ PR ist auch f ◦ g berechenbar.
2
Anmerkung: Es ist f ◦ g(w) := f (g(w)), dabei ist insbesondere f ◦ g(w) undefiniert, wenn g(w) undefiniert ist oder
aber f (g(w)) undefiniert ist.
Die damit exakt definierten Begriffe von Berechenbarkeit und Komplexität kann man nun auch auf notierte Mengen
übertragen:
2.11 Definition. Seien ν1 : Σ∗1 X und ν2 : Σ∗2 Y Notationen von X bzw. Y . Eine Funktion g : X Y heißt
(ν1 , ν2 )–berechenbar , wenn eine Funktion f ∈ PR existiert, so daß für alle w ∈ Σ ∗1 gilt: Existiert g(ν1 (w)), so
auch ν2 (f (w)) und
g(ν1 (w)) = ν2 (f (w)).
2
17
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Die (ν1 , ν2 )–Berechenbarkeit einer Funktion g : X Y kann wie folgt in einem Diagramm veranschaulicht werden:
X
6
ν1
Σ∗1
- Y (ideelle Ebene)
g
6
ν2
f
- Σ∗ (reale Ebene)
2
Die obigen Definition fordert nun, dass das Diagramm für alle w ∈ Def(g ◦ ν 1 ) kommutiert:
g ◦ ν1 (w) = ν2 ◦ f (w).
Gegeben ist also eigentlich w (was wir über ν 1 als Element von X interpretieren). Daraus berechnen wir (mit dem
„realen“ Turingmaschinen–Modell) einen Wert f (w), den wir wiederum als Element von Y und als Wert der Funktion g interpretieren.
2.12 Beispiel. Als wichtiges Beispiel für die Programmierung mit Turingmaschinen betrachten wir binäres Zählen,
hier in Form einer Funktion fbin : IB∗ → mit
fbin (x) = (Länge von x)2
Bei der Angabe der Turingmaschine werden wir ein Flußdiagramm verwenden. Operationen mit nur einem möglichen Folgezustand werden durch ein Rechteck dargestellt, bei zwei möglichen Folgezuständen nehmen wir ein Karo.
Jedem Zustand der Maschine entspricht dabei ein solches Viereck im Flußdiagramm; in den einzelnen Vierecken
steht die jeweils zu auszuführende Operation. Bei δ(z) = (i, x, z 1 , z2 ) steht also i : x im Karo für den Zustand z, die
Verbindung zu z1 und z2 wird mit Pfeilen im Diagramm markiert (Beschriftung ‘ja’ am Pfeil zu z 1 , ‘nein’ am Pfeil zu
z2 ). Der Anfangszustand wird durch einen einlaufenden Pfeil markiert, im Rechteck des Endzustandes steht ‘HALT’.
Da die Bezeichnung der einzelnen Zustände meist unwichtig ist, werden wir im Diagramm die Zustände selbst nur
selten angeben. Die Zahl der Arbeitsbänder und das Arbeitsalphabet werden wir meist gesondert angeben, sie lassen
sich häufig aber auch aus dem Diagramm leicht ablesen.
Das folgende Flußdiagramm definiert damit eine Turingmaschine mit einem Arbeitsband und dem Arbeitsalphabet
∆ = {0, 1, B}:
?
1:0
-
?
E:R
?
Q ja
- Kopie Arbeitsband
Q
E : $Q
Q
auf Ausgabeband
Q
Q
nein
?
?
Erhöhe Inhalt vom
HALT
Arbeitsband um 1
Die beiden „Teilroutinen“ zum Inkrementieren bzw. Kopieren lassen sich wie folgt genauer spezifizieren (am Ende
des Inkrementierens steht der Kopf des Arbeitsbandes am rechten Ende der gespeicherten Daten):
18
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Inkrementieren
-
?
Q nein
1 : 1Q
Q
Q
?
1:0
1:1
-
?
1:R
?
?
Q
nein
1 : BQ
Q
Q
ja
?
1:L
1:L
?
Kopieren
-
?
1:L
?
Q
nein
1 : BQ
Q
Q
ja ?
6
1:R
A:0
A:1
6
ja
?
Q
Q
ja 1 : BQ nein- 1 : 0Q nein
Q
Q
Q
Q
6
?
Hier ergibt sich als Komplexität beispielsweise T M (λ) = 11. Zur Abschätzung der Komplexität im Fall w 6= λ setzen
wir n := lg(w) und wählen k ∈ 0 minimal mit n < 2k ≤ 2n. Insbesondere ist dann sicherlich T M (w) ≤ TM (w̄)
mit lg(w̄) = 2k .
Beim Inkrementieren von 0 bis 2k haben wir dann die folgenden Fälle vorliegen (in Klammern die Zahl des Auftretens
dieser Fälle):
1
( · 2k -fach)
2
1
( · 2k -fach)
4
1
( · 2k -fach)
8
letztes Zeichen wird 1
letztes Zeichen wird 0, vorletztes wird 1
zwei letzte Zeichen werden 0, drittletztes wird 1
...
19
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
also ergibt sich als Schranke für den Gesamtaufwand zum Inkrementieren:
∞
∞
X
X
i (∗)
1 k
k
= 5 · 2k · 2 = 10 · 2k ≤ 20n
·
2
·
(5i)
=
5
·
2
i
2
2i
i=1
i=1
Die Abschätzung (∗) erhalten wir dabei wie folgt: Setze f (p) :=
∞
P
i=1
i · pi−1 , insbesondere also:
∞
P
i=1
i
2i
=
1
2
∞
P
i=1
∞
P
pi =
i=0
i
1 i−1
2
=
1
1
2 (1− 1 )2
2
1
1−p
für 0 < p < 1, also f 0 (p) =
1
(1−p)2
=
= 2.
Beim Kopieren erhalten wir als Schranke für den Aufwand 6k + 2 Schritte, da n in binärer Notation aus k Zeichen
besteht. Damit ist TM (w) ≤ 20 lg(w) + 6 log lg(w) + 2, d.h. M arbeitet in linearer Zeit.
2
2.13 Beispiel. Für die Notation endlicher Alphabete Σ und der jeweiligen Wortmenge Σ ∗ eignen sich „Blockcodes“:
Wähle k mit 2k ≥ n und eine beliebige surjektive Funktion ι : IB k Σ.
Dann kann man νΣ : IB∗ Σ∗ wie folgt definieren: Es sei νΣ (w) := x1 . . . xm (mit xi ∈ Σ), wenn w = ι(x1 ) ◦
ι(x2 ) ◦ . . . ◦ ι(xm ) ◦ w0 für ein w0 ∈ IB∗ mit lg(w0 ) < k gilt, ansonsten sei νΣ (w) undefiniert.
Ist ι eine bijektive Funktion, so ist die resultierende Notation ν Σ damit sogar total.
Wählen wir zum Beispiel Σ = IB, k = 1 und ι(1) = 1, ι(0) = 0, so ergibt sich insbesondere ν IB (w) = w als
„Notation“ der Worte über IB.
2
Im folgenden werden wir uns daher oft auf Σ 1 = Σ2 = IB beschränken können.
Um mehrstellige Funktionen zu betrachten, benutzen wir eine Standardmethode, wie man aus gegebenen Notationen νi von Mengen Xi Notationen von Tupeln aus Elementen dieser Mengen erzeugen kann. Dazu verwenden wir
(möglichst einfach gehaltene) surjektive Abbildungen
pr k : IB∗ → (IB∗ )k (für k ∈ ),
und
pr ∗ : IB∗ →
[
(IB∗ )k
k∈
mit denen wir Worte w in voneinander unabhängige Komponenten pr 1k (w), . . . prkk (w) bzw. pr1∗ (w), pr2∗ (w), . . .
zerlegen können,
Zunächst definieren wir pr k durch die Angabe der einzelnen Komponentenfunktionen (oder Projektionen) pr ik für
1 ≤ i ≤ k: Zur Definition von prik (w) für ein Wort w = x1 . . . xn , xj ∈ IB, wählen wir m ∈ maximal so, daß bei
der Teilfolge
xi xk+i x2k+i x3k+i x4k+i . . .
von w alle vorkommenden x(2j+1)k+i für 0 ≤ j < m den Wert 1 haben. Dann setzen wir
prik (w) := xi x2k+i x4k+i . . . x2(m−1)k+i
Die Werte x(2j+1)k+i sind damit Indikatoren, ob x2jk+i noch „gültig“ ist, w = x1 . . . xn wird also so interpretiert,
daß auf eine Gruppe von jeweils k „Wert-Bits“ eine Gruppe von ebenfalls k „Indikator-Bits“ folgt. pr k sei schließlich
k (w)). Damit ist zum Beispiel
definiert durch pr k (w) := (pr1k (w), . . . , prw
pr 2 (10 11 01 11 10 1) = (101, 01)
Die Funktion pr ∗ definieren wir durch pr ∗ (w) := pr k (w̄) mit k := lg(pr12 (w)) und w̄ := pr22 (w).
20
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Als ‘Inverse’ zu den pr k definieren wir die ‘Paarung’ <w1 , . . . , wk > von Worten:
<w1 , . . . , wk > := das (eindeutig bestimmte) w mit:
pr k (w) = (w1 , . . . , wk ),
lg(w) = 2 · k · max{lg(wi ) | 1 ≤ i ≤ k} und
w enthält möglichst viele 0-en.
Damit ist z.B. <10, 000> = 101100110001.
Anmerkung: (i) Die Surjektivität von pr k und damit auch von pr ∗ ist offensichtlich, da die einzelnen pr ik voneinander unabhängig sind.
(ii) Der Nachweis, daß prik ∈ PR für alle k und i mit i ≤ k gilt, ist eine einfache Programmierübung mit Turingmaschinen.
(iii) Bei der Funktion pr k (w) sind die einzelnen ‘verschlüsselten’ Worte in w verzahnt gespeichert, dazu müssen die
Worte zum Teil künstlich verlängert werden. Als Alternative könnten die Worte einfach hintereinander gespeichert
werden, unter Verwendung eines geeigneten Trennmechanismus (der natürlich auch das Gesamtwort auch verlängert). Die gewählte Variante hat den Vorteil, daß die Komplexität beim Zugriff auf eine der Komponenten pr ik (w)
nur von k und der Länge von prik (w) abhängt, aber völlig unabhängig ist von den anderen Komponenten.
2.14 Beispiel. Sind ν1 , . . . , νk Notationen von Mengen X1 , . . . , Xk , so definieren wir eine Notation
(ν1 , . . . , νk ) : IB∗ X1 × . . . × Xk
durch
(ν1 , . . . , νk )(w) := (ν1 pr1k (w), . . . , νk prkk (w))
Ist ν Notation einer einzelnen Menge X, so definieren wir ν k : IB∗ → X k durch
ν k := (ν, . . . , ν)
| {z }
k
und ν ∗ : IB∗ S
k∈
X k durch
ν ∗ (w) := (ν(v1 ), . . . , ν(vm )),
wobei m und die einzelnen vi sich aus pr ∗ (w) = (v1 , . . . , vm ) ergeben.
2.15 Lemma. Die Addition natürlicher Zahlen + :
2
2
→
2
ist für jedes r in linearer Zeit ((·) 2r , (·)r )-berechenbar.
Beweis: Die Projektionsfunktionen sind so definiert, dass es in linearer Zeit möglich ist, die einzelnen Komponenten
einer Eingabe zu extrahieren und jeweils auf ein eigenes Arbeitsband zu kopieren. Wir werden daher im folgenden
stets implizit annehmen, dass die Argumente so vorliegen. Da führende Nullen den Wert bei r-adischer Notation
nicht verändern, können wir hier zudem annehmen, dass die zwei Argumente v und w, die wir zu addieren haben,
gleich lang sind.
Es seien also v = vk−1 . . . v0 und w = wk−1 . . . w0 aus Σ∗r gegeben, gesucht ist ein Wort u mit (u) r = (v)r + (w)r .
Diese Addition können wir dann analog zur Schulmethode für die Addition von Dezimalzahlen durchführen: Wir
definieren einen „Start-Übertrag“ s 0 := 0 und berechnen wir induktiv die eindeutig bestimmten Werte u i ∈ Σr und
die neuen Überträge si+1 ∈ IB mit ui + r · si+1 = vi + wi + si :
ui := (vi + wi + si ) mod r und si+1 := (vi + wi + si ) div r
21
2 NATÜRLICHE / GANZE ZAHLEN
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
Induktiv ergibt sich 0 ≤ vi +wi +si ≤ 2r −1 und damit auch si+1 ∈ IB. Ebenfalls induktiv ergibt sich (v i . . . v0 )r +
(wi . . . w0 )r = (si+1 ui . . . u0 )r . Wir können das gesuchte u also als u := s k uk−1 . . . u0 definieren.
Die lineare Zeitkomplexität ergibt sich sofort.
2
In der Rechnerhardware (mit r = 2) hat notwendigerweise das Resultat u einer Addition die gleiche Länge wie die
Argumente v und w. Dort wird daher u := u k . . . u0 gesetzt, was natürlich nur bei sk = 0 korrekt ist. Daran kann
also ein Overflow erkannt werden. Zudem erhalten wir dann (u) r = (v)r +(w)r mod 2k . Bei 32-Bit-Rechnern ergibt
sich daher auch i.A. (ohne Warnung!) 0 als Summe von 4294967295 + 1.
Der obige Algorithmus erfordert eine strikte Sequentialisierung der Addition. Bei einer Hardware-Implementierung
bedeuted dies jedoch eine große Schaltkreistiefe und damit eine lange Signallaufzeit, wenn einfache Volladdierer
verwendet werden. Durch Methoden wie Carry-look-ahead kann diese Zeit deutlich reduziert werden [Pa00].
Wenn wir natürliche Zahlen voneinander subtrahieren wollen, ergibt sich das Problem, dass negative Resultate entstehen können. Daher verwenden wir hier folgende Form:
2.16 Korollar. Die Funktionen
x
•
−
y :=
x − y, falls x ≥ y
0,
falls x < y
und
GEQ(x, y) :=
1, falls x ≥ y
0, falls x < y
sind für jedes r in linearer Zeit (((·) r , (·)r ), (·r ))-berechenbar.
2
Beweis: Wie oben können wir uns auf die Betrachtung von v = v k−1 . . . v0 und w = wk−1 . . . w0 aus Σ∗r beschränken. Jetzt setzen wir jedoch
ui := (vi − wi − si ) mod r und si+1 := (vi − wi − si ) div r
woraus sich induktiv (vi . . . v0 )r − (wi . . . w0 )r = (ui . . . u0 )r − si+1 · r i+1 . ergibt. Damit erhalten wir GEQ(x, y) =
sk und (v)r − (w)r = (uk−1 . . . u0 )r im Falle von sk = 0.
2
2.17 Beispiel. Die Addition- und Subtraktionsroutinen für gleich lange Worte finden sich auch in der GMP[Gr02].
Wir betrachten hier nur die “generische“ C-Fassung dieser Routinen, die auf Assembler-Code verzichtet. Als Alphabet Σr wird dabei im Falle von 32-bit-Rechnern der volle Umfang von unsigned long int verwendet, hier
„limb“ als Verallgemeinerung von „digit“ genannt: typedef unsigned long int mp_limb_t.
Das folgende Programm ist ein leicht modifizierter Auszug aus der Datei aors_n.c, in der diese maschinenungebunde Fassungen von Addition und Subtraktion eingeführt werden. Die Bibliothek enthält jedoch auch für viele
CPU-Typen handoptimierte Assembler-Routinen.
mp_limb_t
mpn_add_n (mp_ptr res_ptr, mp_srcptr s1_ptr, mp_srcptr s2_ptr, mp_size_t size)
{
register mp_limb_t x, y, cy;
register mp_size_t j;
...
cy = 0;
do
{
y = s2_ptr[j];
x = s1_ptr[j];
y += cy;
/* previous carry/borrow into second operand */
22
2 NATÜRLICHE / GANZE ZAHLEN
cy = (y < cy);
y = x + y;
cy += (y < x);;
res_ptr[j] = y;
2.1 Notationen, natürliche Zahlen und Berechenbarkeit
/* new carry from that
/* add
/* and its carry
*/
*/
*/
}
while (++j != 0);
return cy;
}
Interessant ist dabei, wie die (hier regelmäßigen!) Overflows bei der Addition von limbs durch einen anschließenden
Größenvergleich abgefangen werden. Viele (aber nicht alle!) Prozessortypen bieten als Maschinen-Operation bereits
analoge Addition oder Subtraktion mit Carry an, die aber in der Regel aus Hochsprachen nicht zugänglich sind. 2
Wie bereits angegeben, ist die Menge PR der berechenbaren Funktionen unter Substitutionen abgeschlossen. Daraus
ergibt sich die Transitivität der Berechenbarkeit bzgl. Notationen:
2.18 Satz. Seien ν1 , ν2 , ν3 Notationen von M1 , M2 , M3 , sei g : M1 M2 und h : M2 M3 . Ist g (ν1 , ν2 )–berechenbar
2
und ist h (ν2 , ν3 )–berechenbar, so ist h ◦ g : M1 M3 (ν1 , ν3 )–berechenbar.
Beweis: Seien f1 , f2 ∈ PR gegeben mit
∀w ∈ Def(gν1 ) gν1 (w) = ν2 f1 (w),
∀v ∈ Def(hν2 ) hν2 (w) = ν3 f2 (w),
Dann gilt für alle w ∈ Def(h ◦ g ◦ ν1 ) ⊆ Def(g ◦ ν1 )
h ◦ g ◦ ν1 (w) = h ◦ (ν2 ◦ f1 (w))
= ν3 ◦ f2 ◦ f1 (w).
(⇒ f1 (w) ∈ Def(h ◦ ν2 ))
Mit f2 ◦ f1 ∈ PR ist h ◦ g also (ν1 , ν3 )–berechenbar.
2
Bei den als Beispiel verwendeten Notationen sind die Definitionen zum Teil recht willkürlich, zum Teil auch nicht
vollständig ausgeführt. Schon kleine Modifikationen an den Definitionen führen zu anderen Notationen, etwa die Änderung von ι bei νΣ . Es stellt sich also die Frage, wie man zwei Berechenbarkeitsbegriffe auf einer Menge vergleicht,
die von zwei verschiedenen Notationen herrühren:
2.19 Definition. Seien ν, ν 0 Notationen einer Menge X.
(a) ν 0 heißt auf ν reduzierbar ( in Zeichen ν 0 ≤ ν), wenn ein f ∈ PR existiert mit
(∀w ∈ Def(ν 0 )) ν 0 (w) = νf (w).
(b) ν 0 und ν heissen (berechenbar) äquivalent (in Zeichen ν 0 ≡ ν), wenn ν 0 ≤ ν und ν ≤ ν 0 gilt.
2
Anmerkung: Bei ν 0 ≤ ν kann man also Namen bzgl. ν 0 mit einem f ∈ PR in Namen bzgl. ν übersetzen. Ferner ist
„ν 0 ≤ ν“ gleichwertig mit „idX ist (ν 0 , ν)-berechenbar“ für die Identität id X : X → X, idX (x) := x. Damit ergibt
sich sofort:
23
2 NATÜRLICHE / GANZE ZAHLEN
2.2 Ganze Zahlen
2.20 Satz. Seien ν1 , ν2 äquivalente Notationen einer Menge X und ν 0 Notation einer weiteren Menge X 0 . Dann gilt:
(a) g : X X 0 ist (ν1 , ν 0 )-berechenbar genau dann, wenn g (ν 2 , ν)-berechenbar ist.
(b) h : X 0 X ist (ν 0 , ν1 )-berechenbar genau dann, wenn h (ν 0 , ν2 )-berechenbar ist.
2
Modifikationen bei der Definition einer Notation sind also erlaubt und ohne (wichtige) Auswirkungen, wenn dabei
äquivalente Notationen entstehen.
2.21 Lemma. Die Notationen (·)r sind alle äquivalent!
2
Beweis: als Übung
2.2
Ganze Zahlen
Ganze Zahlen werden in Rechnerhardware meist in der „Zweierkomplement-Notation“ notiert:
2.22 Definition. Die „Zweierkomplement-Notation“ (·) zk : IB∗ \ {λ} → ist definiert durch:
k−1
X
(vk−1 . . . v0 )2
falls vk = 0
(vk vk−1 . . . v0 )zk :=
=
vi · 2 i − v k · 2 k .
−2k + (vk−1 . . . v0 )2 falls vk = 1
i=0
2
Beispiele sind (010101)zk = (10101)2 = 21, (101010)zk = −25 + (01010)2 = −22. Es ergibt sich:
2.23 Lemma. Für alle k ∈
gilt:
(IBk )zk = {x ∈ = {x ∈ | 0 ≤ x ≤ 2 k − 1 ∨ 0 − 2 k ≤ x ≤ 2k − 1 − 2k }
| −2k ≤ x ≤ 2k − 1}
2
Damit ist (·)zk sicher surjektiv. Ferner kann man am ersten Zeichen eines Wortes v = v k . . . v0 das Vorzeichen von
(v)zk ablesen: Es gilt (v)zk ≥ 0 ⇔ vk = 0.
Normalerweise kann die Länge der Worte bei der Zweierkomplementdarstellung im Computer nur einige wenige,
feste Werte annehmen; in C++ sind dies beim Typ short int 16 Bit (= 2 Byte) und beim Typ long int 32 Bit
(= 4 Byte). Damit ergeben sich folgende Bereiche, die dargestellt werden können:
short int −215 .. 215 − 1, d.h. − 32768 .. 32767
long int
−231 .. 231 − 1, d.h. − 2.147.483.648 .. 2.147.483.647
Der entscheidende Vorteil der Zweierkomplement-Notation
ist die technisch einfache Realisierung von Addition
Pk−1
P
i
k
(und Subtraktion): Da (vk . . . v0 )zk = i=0 vi 2 − vk 2 = ki=0 vi 2i − vk 2k+1 = (vk . . . v0 )2 − vk 2k+1 gilt, kann
man die Zweierkomplementaddition auf die normale Addition von Dualzahlen zurückführen: Es seien v = v k . . . v0
und w = wk . . . w0 gegeben. Zur Addition von (v)zk und (w)zk führen wir zunächst eine normale Dualaddition von
vk . . . v0 und wk . . . w0 mit Ergebnis uk . . . u0 =: u und letztem Übertrag sk+1 durch, d.h. es ergibt sich (v)2 +
(w)2 = (u)2 + sk+1 2k+1 . Damit folgt
(v)zk + (w)zk = (v)2 − vk 2k+1 + (w)2 − wk 2k+1
= (u)2 + 2k+1 (sk+1 − vk − wk )
= (u)zk + 2k+1 (uk + sk+1 − vk − wk )
Auf jeden Fall gilt also (v)zk + (w)zk mod 2k+1 = (u)zk mod 2k+1 . Wir unterscheiden nun zwei Fälle:
24
2 NATÜRLICHE / GANZE ZAHLEN
2.2 Ganze Zahlen
• Die Summe (v)zk + (w)zk ist im Zweierkomplement als Wort der Länge k+1 darstellbar: Dann gilt −2 k ≤
(v)zk + (w)zk < 2k ,−2k ≤ (u)zk < 2k und damit |(u)zk − ((v)zk + (w)zk )| < 2k+1 . Es folgt (u)zk =
(v)zk + (w)zk und uk + sk+1 = vk + wk .
• Die Summe (v)zk + (w)zk ist nicht mit nur k+1 Zeichen darstellbar (d.h. es liegt ein overflow vor): Es sei also
(v)zk + (w)zk < −2k oder (v)zk + (w)zk ≥ 2k . Wegen −2k ≤ (u)zk < 2k ergibt sich uk + sk+1 6= vk + wk .
Overflow liegt genau dann vor, wenn u k + sk+1 6= vk + wk gilt, ansonsten ist (u)zk = (v)zk + (w)zk .
Eine alternative Notation der ganzen Zahlen benutzt stellt Vorzeichen und Betrag der Zahl getrennt dar („signmagnitude“):
2.24 Definition. Die „sign-magnitude-Notation“ (·) sm : IB∗ \ {λ} → ist definiert durch:

falls v = 0w für ein w ∈ IB∗
 (w)2 ,
−(w)2 , falls v = 1w für ein w ∈ IB∗
(v)sm =

0
falls v = λ
2
Das erste Bit eines Wortes stellt also das Vorzeichen dar. Der Wertebereich ergibt sich zu
2.25 Lemma. Für alle k ∈
gilt(IB k+1 )sm = {x ∈ | 1 − 2k ≤ x ≤ 2k − 1}.
2
Insbesondere hat dann die Zahl Null auch bei jeder festen Wortlänge zwei Namen: 000 . . . und 100 . . ..
Diese Notation wird oft benutzt, wenn die Wortlänge nicht fixiert ist, sondern variabel bleibt (d.h. bei SoftwareLösungen). Als Beispiel kann hier GMP (GNU Multiple Precision Arithmetic Library) dienen. Die entsprechende
Deklaration in GMP.h lautet:
typedef struct
{
int _mp_alloc;
int _mp_size;
mp_limb_t *_mp_d;
} __mpz_struct;
/* Number of *limbs* allocated and pointed
to by the _mp_d field. */
/* abs(_mp_size) is the number of limbs the
last field points to. If _mp_size is
negative this is a negative number. */
/* Pointer to the limbs. */
2.26 Lemma. (·)zk und (·)sm sind äquivalent. Die Umrechnung ist in linearer Zeit möglich.
Die Addition und die Subtraktion ganzer Zahlen sind in linearer Zeit ((·) 2sm , (·)sm )-berechenbar.
2
Beweis: Bei gegebenem v = (vk . . . v0 ) gilt P
im Falle vk = 0 sofort
(v)zk = (v)
. Setzen wir wi = 1 − vi , so
Pk−1
Psm
k−1
k−1 i
k
i
k
i
erhalten wir für vk = 1 sofort (v)zk = −2 + i=0 vi 2 = −2 − i=0 wi 2 − i=0 2 = −(1 + (wk−1 . . . w0 )2 )
Pk−1 i Pk−1
Pk−1 i
und (v)sm = − i=0
vi 2 = i=0 wi 2i − i=0
2 = −2k + 1 + (wk−1 . . . w0 )2 . Ein Algorithmus zum Nachweis
der Äquivalenz muß also lediglich Bits invertieren und dann binär 1 addieren oder subtrahieren.
Die Algorithmen zur Addition und Subtraktion könnte man durch Umwandlung ins Zweierkomplement ausführen.
Es ist jedoch schneller, entsprechend der Vorzeichenkombination der Argument eine einfache Binäraddition oder
Subtraktion durchzuführen. Tritt der Fall ein, dass bei einer Binärsubtraktion das Ergebnis negativ würde, so vertauscht man die zwei Argumente, subtrahiert erneut und korrigiert dies über das Vorzeichen des Resultates.
2
25
2 NATÜRLICHE / GANZE ZAHLEN
2.3
2.3 Die Multiplikation natürlicher Zahlen
Die Multiplikation natürlicher Zahlen
Während für Additionen ganzer Zahlen die Zweierkomplement-Notation von Vorteil ist, ist bei Multiplikationen und
Divisionen die Sign-Magnitude-Notation einfacher in der Handhabung. Hier kann man nun zwei Teile unterscheiden:
Die (triviale) Bestimmung des Vorzeichens des Resultates und die Multiplikation (oder Division) der natürlichwertigen Anteile der Argumente. Aus diesem Grund werden wir im folgenden auch nur die Multiplikation natürlicher
Zahlen untersuchen.
Die Computer-Hardware wird wieder auf binärer Darstellung aufbauen (müssen), während Softwarelösungen wie
GMP diese Hardware-Multiplikationen als Basisoperationen verwenden können. Hier ist es also auch wieder sinnvoll, die r-adischen Zahlen mit großer Basis r zugrunde zu legen (d.h. r = 2 32 bei 32-Bit-Rechnern), da dann die
Algorithmen im praktischen Einsatz wesentlich schneller werden.
Wie Multiplikationen in Hardware durchgeführt werden können, werden wir nur am Rande streifen. In [Pa00] wird
dieses Thema ausführlich behandelt.
P
Sind v = vk−1 . . . v0 und w = wk−1 . . . w0 gegeben, so erhalten wir (v)r ·(w)r = 0≤i<k wi r i ·(v)r . Damit können
wir zunächst die Multiplikation mit (w) r auf die Multiplikationen von (v)r mit den einzelnen Stellen wi reduzieren,
deren Resultate dann nach entsprechendem Links-Shifts (für die Multiplikationen mit den Potenzen r i von r) addiert
werden.
Für die Einzel-Multiplikationen brauchen wir zusätzlich noch das „kleine Ein-mal-Eins“, d.h. eine Tabelle (oder
Funktion) (x, y) 7→ (u, s) mit x · y = s · r + u für x, y, u, s ∈ Σ r .
2.27 Lemma. (Multiplikation mit Konstanten)
Eine natürliche Zahl kann in r-adischer Notation mit einer konstanten Zahl in linearer Zeit multipliziert werden. 2
Beweis: Wir betrachten zunächst den Fall (v k−1 . . . v0 )r · m mit einer Zahl m ∈ Σr . Ähnlich wie bei der Addition
setzen wir s0 := 0 und induktiv ergeben sich mit dem kleinen Ein-mal-Eins u i , si+1 aus si+1 · r + ui = vi · m + si .
Allerdings ist hier nicht mehr si ∈ IB, stattdessen kann si beliebige Werte aus Σr annehmen. Wegen vi · m + si ≤
(r−1)2 + r−1 = (r−1) · r bleibt der Übertrag jedoch stets einstellig.
Induktiv ergibt sich (vi . . . v0 )r · m = (ui . . . u0 )r + si+1 · r i+1 , d.h. als Produkt ergibt sich (sk uk−1 . . . u0 )r .
Ist der Multiplikator mehrstellig, so zerlegen wir in in seine einzelnen Stellen, die wir getrennt multiplizieren und
jeweils direkt aufaddieren. Bei festen Multiplikator ergibt sich also insgesamt ein linearer Aufwand.
2
Anmerkung: Bei den üblichen PC-Prozessoren gibt es einen Assemblerbefehl, der zwei 32-Bit-Zahlen multipliziert
und eine 64-Bit-Zahl als Resultat ergibt. Beim ebenfalls vorhandenen Multiplikationsbefehl mit 32-Bit-Ergebnis werden wie üblich die oberen 32 Bit abgeschnitten. In der GNU-C-Bibliothek (und entsprechend dem GCC-Compiler)
können die 64-Bit-Werte als unsigned long long int angesprochen werden.
Es ergibt sich sofort:
2.28 Lemma. (Schul-Methode zur Multiplikation)
Natürliche Zahlen können in r-adischer Notation in quadratischer Zeit O(n 2 ) multipliziert werden.
2
Im Spezialfall r = 2 ist das kleine Ein-mal-Eins ziemlich trivial, daher reduziert sich die Multiplikation dort auf
folgende einfache Additionen:
X
(v0i )2
(v)2 · (w)2 =
i:wi =1
Weitere Möglichkeiten, die Multiplikation auf der Hardware-Seite, d.h. mit fester Wortlänge k, zu optimieren, findet
man in [Pa00]. Wir werden im folgenden die Asymptotik für k → ∞ untersuchen, wo wesentlich bessere Resultate
als die gezeigte quadratische Laufzeit zu erreichen sind.
26
2 NATÜRLICHE / GANZE ZAHLEN
2.3 Die Multiplikation natürlicher Zahlen
Wir beginnen mit dem Algorithmus des russischen Mathematikers Karatsuba, 1963 veröffentlicht. Die Grundidee
besteht darin, die Multiplikation langer Zahlen rekursiv auf kürzere Multiplikationen zurückzuführen, unter Inkaufnahme einiger zusätzlicher Schiebe- und Additionsoperationen.
2.29 Lemma. (Karatsuba-Algorithmus)
Natürliche Zahlen können in r-adischer Notation in Zeit O(n log2 3 ) ≈ O(n1.58496 ) multipliziert werden.
2
Beweis: Seien v und w gegeben, wobei wir o.B.d.A annehmen, dass sie gleiche gerade Länge 2n haben (evtl. durch
führende Nullen). Es ist als v = v2n−1 . . . v0 und w = v2n−1 . . . v0 . Wir zerlegen beide in zwei Hälften:
vh := v2n−1 . . . vn
wh := w2n−1 . . . wn
Damit ist z.B. (v)r = (vh )r ·
rn
vl := vn−1 . . . v0
wl := wn−1 . . . w0
+ (vl )r
Somit haben wir für das Produkt beider Zahlen die Gleichung
(v)r · (w)r
= (r 2n +r n ) · ((vh )r · (wh )r )
+r n · ((vh )r − (vl )r ) · ((wl )r − (wh )r )
+(r n +1) · ((vl )r · (wl )r )
Diese Gleichung reduziert die Multiplikation zweier 2n-stelliger Zahlen auf drei Multiplikationen von n-stelligen
Zahlen, da sich die „überzähligen“ Terme genau aufheben. Diese n-stelligen Multiplikationen können nun entweder
rekursiv über die gleiche Formel oder aber direkt mit der obigen „Schul-Methode“ berechnet werden.
Wenn T (n) die benötigte Zeit für eine Multiplikation n-stelliger Zahlen ist, ergibt sich also T (2n) ≤ 3T (n) + cn.
Wählt man c0 = 3T (1) + c, so ergibt sich induktiv T (2 k ) ≤ c0 (3k − 2k ), also sicherlich T (2k ) ≤ c0 3k . Es folgt
T (n) ≤ T (2dlog2 ne ≤ c0 3dlog2 ne ≤ c0 31+log2 n ≤ 3c0 2log2 n·log2 3 = 3c0 nlog2 3
2
Die Karatsuba-Methode ist nur eine Spezial-Form einer allgemeineren Vorgehensweise, bei der man eine Zahl in
d + 1 kürzere Komponenten zerlegt, die mittels 2d + 1 Multiplikationen und durch geeignete Linearkombinationen
zusammengesetzt das Gesamt-Produkt ergeben. Die Grundidee dieser Methode stammt vom russischen Mathematiker A.L. Toom; S.A. Cook zeigte 1966, wie diese Idee für schnelle Computerprogramme eingesetzt werden konnte:
2.30 Lemma. (Toom-Cook-Algorithmus mit fester Teilung)
Für jedes gegebene ε > 0 können natürliche Zahlen in r-adischer Notation in Zeit c(ε)n 1+ε multipliziert werden.
Dabei ist c(ε) von n unabhängig.
2
Einschub:
Toom-Cook-Multiplikationsmethode
Wählt man die Anzahl d+1 der Teile, in die man die Argumente v und w zerlegt, in Abhängigkeit von der Länge von
v und w, so läßt sich die asymptotische Entwicklung der Komplexität weiter nach unten drücken. Allerdings muß
man dann auch die Polynom-Koeffizienten explizit berechnen (statt sie wie oben einer festen Tabelle entnehmen zu
können). Daher werden statt der Lagrange-Interpolationsformel auch Differenzenschemata zur Interpolation verwendet. Damit lässt sich die aymptotische Schranke für die Komplexität der Multiplikation weiter sehr stark drücken:
27
2 NATÜRLICHE / GANZE ZAHLEN
2.4 Divisions-Algorithmen
2.31 Lemma. (Toom-Cook-Algorithmus mit variabler Teilung, ohne Beweis,
vgl. [Kn81])
√
2
log
n
) multipliziert werden.
Natürliche Zahlen können in r-adischer Notation in Zeit O(n log n · 2
2
Verwendet man statt der hier benutzten r−adischen Notation in Zwischenschritten eine „modulare“ Notation, so kann
man noch schneller multiplizieren: Der schnellste bekannte Algorithmus zur Multiplikation mit dem TuringmaschinenModell stammt von Schönhage und Strassen:
2.32 Lemma. (Schönhage-Strassen-Algorithmus, ohne Beweis, vgl. [Kn81])
Natürliche Zahlen können in r-adischer Notation in Zeit O(n log n · log log n) multipliziert werden.
2
Es ist nicht bekannt, ob bei dem von uns benutzten Turing-Maschinen-Modell schneller multipliziert werden kann als
angegeben. Auf anderen Rechnermodellen (pointer machine, storage modification machine [Sch80]) ist es allerdings
sogar möglich, in linearer Zeit zu multiplizieren.
Als untere Schranke gibt es für den allgemeinen Fall nur die (triviale) lineare Laufzeit. Für spezielle Berechnungsmodelle (online-Arithmetik) gibt es die untere Schranke n log n/ log log n [PFM74]
Der Karatsuba-Algorithmus, einfache Fassungen des Toom-Cook (Zerlegung in drei Teile) und die SchönhageStrassen-Methode sind z.B. in der GMP-Bibliothek implementiert. Eine Tuning-Routine aus GMP 4.0 lieferte zum
Beispiel für Pentium-III-5ooMHz CPUs, dass es sinnvoll wäre, Karatsuba ab n = 23, Toom-Cook ab n = 139 und
die Schönhage-Strassen-Methode ab n = 4864 einzusetzen. (Für einen 900-MHz-Athlon ergaben sich als Werte
analog: n = 26, 177, 9472.)
Da die Komplexität der Multiplikation noch nicht eindeutig bestimmt werden konnte, werden wir im folgenden einfach eine Funktion M dafür ansetzen. Bei heutigen Kenntnisstand wäre also M(n) = n log n log log n. Wir werden
bei der Definition zwei Bedingungen an diese Komplexitätsschranke stellen, die erstmals bei [PFM74] auftauchen
und benutzt werden, um Glattheitseigenschaften der Schranke vorzuschreiben. In ähnlicher Bedeutung werden diese Bedingungen öfter benutzt. Wir werden sie erst später motivieren können, da wir hier den Beweis der unteren
Schranke nicht weiter verfolgen.
→ sei im folgenden die beste (bekannte) Komplexitätsschranke für die Multiplikation
2.33 Definition. M :
von natürlichen Zahlen bei r-adischer Notation und dem Turingmaschinen-Modell mit beliebiger fester Zahl von
Arbeitsbändern. Wir verlangen zudem, dass M monoton ist und 2M(n) ≤ M(2n) ≤ cM(n) + c für alle n ∈
und eine Konstante c gilt.
2
2.4
Divisions-Algorithmen
Im Bereich der natürlichen Zahlen ist unter einer Division immer einer Division mit Rest zu verstehen, d.h. bei
gegebenem y 6= die Zerlegung einer Zahl x in zwei Zahlen p und q < y mit x = p · y + q, d.h. p = b xy c,
q = x mod y.
Wir werden in einem späteren Kapitel zeigen, dass die Division so schnell ausgeführt werden kann wie eine Multiplikation. Zunächst werden wir jedoch die Schul-Methode zur Division analysieren, bei der man eine Stelle des
Quotienten „errät“ und dann entsprechend den Dividenden korrigiert.
Als Basis-Operation brauchen wir hier die Möglichkeit, zweistellige Zahlen durch einstellige Zahlen mit Rest zu
dividieren. Genauer: wir nehmen an, dass wir zu v, w ∈ Σ r , w 6= 0 und s < w Werte u ∈ Σr und s0 < w bestimmen
können, so daß
u · w + s0 = (sv)r
Bei r = 2 ist dies trivial: Hier ist nur w = 1 und damit s = 0 von Interesse, also muß hier s 0 = 0 und u = v gelten.
Wir betrachten zuerst den Fall, dass der Divisor nur einstellig ist:
28
2 NATÜRLICHE / GANZE ZAHLEN
2.4 Divisions-Algorithmen
2.34 Lemma. (Schul-Methode zur Division, einstellig)
Division mit Rest ist bei natürlichen Zahlen in r-adischer Notation bei einem einstelligen Divisor in Zeit O(n)
möglich.
2
Beweis: Wir betrachten die Division einer als (v) r gegebenen Zahl durch ein w ∈ Σr Der Einfachheit
indiP halber
k−i
zieren wir die einzelnen Stellen von v jetzt von links nach rechts, also v = v 0 . . . vk−1 mit (v)r = vi r .
Wir setzen zunächst s0 = 0 und dann wieder iterativ ui und si so daß ui · w + si+1 = (si vi )r : Hier gilt dann
(v0 . . . vi )r = (u0 . . . ui ) · w + si+1
Insgesamt erhalten wir also (v0 . . . vk−1 )r div w = (u0 . . . uk−1 )r Rest sk
2
Hat der Divisor mehr als eine Stelle, so können wir zwar den gleichen Ablauf wie bei der einstelligen Division
verwenden, aber wir brauchen eine mächtigere Basisoperation: Zu v ∈ Σ r , w ∈ Σkr , w 6= 0 und s ∈ Σkr mit
(s)r < (w)r brauchen wir Werte u ∈ Σr und s0 ∈ Σkr mit (s0 )r < (w)r , so daß u · (w)r + (s0 )r = (sv)r .
Diese bedeutet i.W., dass wir zu s, v, w dasjenige u ∈ Σ r finden müssen, für das u · (w)r ≤ (sv)r < (u + 1) · (w)r .
Hieraus lässt sich dann das gesuchte s 0 durch Berechnung von (sv)r − u · (w)r bestimmen. Beim Erlernen der
Division in der Schule wurde dieser Wert von u einfach geraten. In [Kn81] findet man eine einfache deterministische
Methode, mit der man einen Wert û bestimmen kann, der fast immer passt. Es sei dazu s = s 0 s1 . . . sk .
2.35 Lemma. Setze û = min {(s0 s1 )r /w0 , r−1}. Dann gilt u ≤ û.
Ist zudem w0 ≥ br/2c, so gilt sogar û−2 ≤ u ≤ û.
2
Beweis:
Einschub:
Quotienten-Bestimmung
2
Die Bedingung w0 ≥ br/2c lässt sich erzwingen, indem man w (und natürlich auch v) entsprechend mit br/(w 0 +1)c
erweitert. Man muß danach also nur noch maximal 3 drei Werte für u austesten. Pro Stelle des Ergebnisses bei der
Division ist dies mit einem Aufwand durchzuführen, der linear in der Länge von w ist. Damit ergibt sich sofort:
2.36 Lemma. (Schul-Methode zur Division)
Division mit Rest von (v0 . . . vk−1 )r und (w0 . . . wn−1 )r ist in Zeit O(k · n) möglich.
2
Ist n das Maximum der Längen von Dividend und Divisor, so ist die Division damit in Zeit O(n 2 ) möglich.
Schnellere Algorithmen zur Division lassen sich besser im Bereich reeller Zahlen motivieren, daher werden wir sie
erst später behandeln. Wir geben hier jedoch schon einmal das Ergebnis an, das im wesentlichen darauf zurückzuführen ist, dass ein Kehrwert 1/a durch die Bestimmung des Fixpunktes der Funktion Φ(x) = 2x − ax 2 berechnet
werden kann:
2.37 Lemma. (Konvergenz-Methode zur Division, Beweis folgt später)
Division mit Rest von (v0 . . . vk−1 )r und (w0 . . . wn−1 )r ist in Zeit O(M(k + n)) möglich.
29
2
2 NATÜRLICHE / GANZE ZAHLEN
2.5 Redundante Zahl-Notationen
Die folgende Graphik zeigt f (x) = 2x − ax 2 für den Spezialfall a = 3, wo man die sehr gute Konvergenz der
Fixpunktiteration bereits erkennen kann, da hier die Ableitung im Fixpunkt x = 1/3 sogar 0 ist:
Bestimmung des Kehrwertes von a=3
0.4
2*x-a*x*x
0.38
0.36
0.34
0.32
0.3
0.3
2.5
0.32
0.34
0.36
0.38
0.4
Redundante Zahl-Notationen
Der wichtigste Punkt beim Nachweis der Surjektivität r-adischer Notationen war die Eigenschaft, dass man jede
Zahl x ∈ in der Form x = q · r + p mit q ∈ und p ∈ Σ r schreiben kann. Dies ist jedoch auch möglich, wenn man
andere Ziffernmengen verwendet: Bereits 1961 untersuchte Avizienis die Ziffernmenge {−1, 0, 1} bei Basis r = 2
auf ihre Eignung zu carry-freier Addition.
Man spricht hier von signed digits, wobei man im allgemeinen für eine gegebene Basis r die Ziffern {d | r > |d|}
meint.
Damit gibt es nun offensichtlich mehrere Möglichkeiten der Zerlegung x = q · r + p: Wenn x = q · r + d gilt, so
ist auch x = (q+1) · r + (d−r). Da man jede natürliche Zahl n r-adisch hinschreiben kann, kann man sie damit
trivialerweise auch im Ziffern-System {d | r > |d|} notieren. Ebenso kann man natürlich auch −n notieren, indem
man bei der jedes Ziffer d durch die Ziffer −d ersetzt. Man erhält also eine Notation (·)r von .
Die Idee des folgenden Algorithmus zur Carry-freien Addition findet sich bereits in [Av61]: Sind zwei Zahlen
x = (. . . xj xj−1 . . .)r und y = (. . . yj yj−1 . . .)r in einem signed digit-Zahlsystem mit Basis r gegeben, werden
zunächst zwei neue Folgen von Transferziffern (. . . t j tj−1 . . .) und Zwischensummen (. . . wj wj−1 . . .) berechnet.
Die Transferstellen werden um 1 geshifted (d.h. u j := tj−1 ) schließlich summiert man sj := wj + uj mit Resultat
(. . . sj sj−1 . . .)r = x + y.
Genauer:
r · tj + wj := xj + yj
Wegen der Redundanz im Zahlensystem sind dadurch t j und wj nicht eindeutig definiert, wir setzen z.B.
xj + yj ≥ r/2
⇒ tj := 1,
wj := xj + yj − r
|xj + yj | < r/2
⇒ tj := 0,
wj := xj + yj
xj + yj ≤ −r/2 ⇒ tj := −1, wj := xj + yj + r
Es ist leicht nachweisbar, dass (. . . s j sj−1 . . .)r = x+y gilt. Die einzige fehlende Stelle ist noch der Nachweis, dass
sj eine erlaubte Ziffer ist. Im Falle der üblichen vollen Ziffernmenge x j , yj ∈ {1−r, . . . , r−1} ist die notwendige
Bedingung hier |wj | ≤ r − 2. Wir brauchen also r > 2. Für r = 2 gibt es allerdings ebenfalls die Möglichkeit,
fast Carry-frei zu addieren, allerdings braucht man (wenige) zusätzliche Shift-Operationen. Beispiele für solche
Algorithmen finden sich in [Pa00].
In theoretischen Arbeiten zur Berechenbarkeit auf reellen Zahlen wurden signed digits eingesetzt, um die Komplexität reeller Funktionen definieren zu können, z.B. [Mu86, Mu87]. Dabei muß eine Normalisierungsbedingung
beachtet werden: Um topologisch kompakte Namensmengen reeller Zahlen zu erhalten, dürfen die führenden zwei
Ziffern eines Namens für r = 2 weder ‘1 −1’ noch ‘−1 1’ sein, beide könnten zu ‘0 1’ bzw. ‘0 −1’ vereinfacht
30
2 NATÜRLICHE / GANZE ZAHLEN
2.6 Modulare Arithmetik
werden. Bei einer Basis r > 2 sind als Normalisierungsbedingung die Folgen ‘1 1−r’ und ‘−1 r−1’ als führende
Werte verboten.
Leider erzeugt der Additionsalgorithmus in der obigen Form solche „verbotenen“ Folgen: Ein Beispiel (passend für
beliebiges r > 3) ist die Addition von (. . . 0 3 1−r 1−r . . .) und (. . . 0 −1 1−r 1−r . . .) mit Summe (. . . 0 1 1−r . . .).
Eine mögliche Lösung ist es, die Ziffern r−1 und 1−r ganz zu verbieten. Man kann sogar soweit gehen, sich auf die
r+1
r+1
Ziffern Dr := {−d r+1
2 e, ..., 0, ..., d 2 e} zu beschränken. Hier gilt d 2 e < r−1, sobald r > 4. Dieser Ziffernsatz
ist immer noch ausreichend groß, um die Surjektivität der Notation zu gewährleisten. Auch die Carry-freie Addition
ist noch möglich: Beim obigen Algorithmus ergibt sich |x i +yi | ≤ r+2 , damit |wi | ≤ d r+1
2 e − 1 und sj ∈ Dr .
2.38 Lemma. (a) Trotz der Redundanz der Notation bestehen die Namen für die Zahl Null nur komplett aus Nullen.
(b) Das Vorzeichen einer Zahl ist das Vorzeichen der ersten Ziffer der Namens, die nicht die Ziffer Null ist.
r+1
(b) Beim Ziffernsatz Dr := {−d r+1
2 e, ..., 0, ..., d 2 e} mit r > 4 lässt sich die Größe einer Zahl aus der Position
der ersten Nicht-Null-Ziffer abschätzen: Für jede Folge (. . . x j xj−1 . . .) gilt
X
m+1
j
r
>
xj · r > r m−1
j∈ ,j≤m
wenn m die Position der ersten Ziffer ist, die nicht Null ist.
2
Durch die Möglichkeit Carry-freier Addition verringern sich die Abhängigkeiten zwischen den einzelnen Stellen
der Argumente und des Ergebnisses der Operation. Dies kann man bei Vektorrechnern für ein besseres Pipelining
der Daten ausnutzen. Da auch Alltags-CPUs jedoch mittlerweile ähnliche Eigenschaften aufweisen, könnte die Verwendung der redundanten Darstellung zu einem deutlichen Geschwindigkeitsvorteil führen. Die Beschränkung auf
die minimale Ziffernmenge Dr vermeidet zudem Nachteile dieser Notation durch evtl. fehlende Normierung. Eine
entsprechende Implementierung ist jedoch noch nicht bekannt, ein erster Ansatz wird in [Mu02] beschrieben.
2.6
Modulare Arithmetik
Statt der vom Dezimalsystem gewohnten Ziffer-Schreibweise kann man Zahlen auch unter Verwendung des chinesischen Restsatzes notieren. Grundidee ist die Verwendung mehrerer Moduli m 1 , m2 , m3 , . . ., die wechselseitig
teilerfremd sind. Eine natürliche Zahl x wird dann notiert durch die Reste x 1 = x mod m1 , x2 = x mod m2 , . . .:
2.39 Definition. Bei einer gegebenen Folge M = (m 1 , m2 . . .) teilerfremder Zahlen definieren wir eine Notation
νM der natürlichen Zahlen durch
νM (w) := x
⇔ pr ∗ (w) = (v1, v2, ..., vm ) und x = die eindeutig bestimmte Zahl ≤
Qm
i=1 mi
mit (vi )2 = x mod mi .
2
Die Eindeutigkeit der Zahl x in der obigen Definition ist der Kern des genannten chinesischen
Restsatzes (ca. 400
Qm
a.D.!) Er lässt sich z.B. durch ein einfaches Zählargument beweisen: Mit M (m) := i=1 mi gibt es genau M (m)
Zahlen x mit 0 ≤ x < M (m) und ebenfalls genau M (m) mögliche Tupel von Resten x mod m i .
Die Grundrechenarten Addition, Subtraktion und Multiplikation können nun sehr einfach auf die einzelnen „Ziffern“
xi := (vi )2 übertragen werden, wenn die Argumente mit gleicher Zahl m von Moduln gegeben sind und solange
dabei kein Overflow passiert, d.h. das Ergebnis einer Operation negativ wird oder größer als M (m) wird:
31
2 NATÜRLICHE / GANZE ZAHLEN
2.7 Kompakte Notationen
Ist xi = x mod mi und yi = y mod mi , so gilt
(x + y) mod mi = (xi + yi ) mod mi
(x − y) mod mi = (xi − yi ) mod mi
(x · y) mod mi = (xi · yi ) mod mi
Damit können alle Einzelreste unabhängig voneinander und damit evtl. parallel berechnet werden.
Mit Hilfe elementarer Arithmetik für die Binärnotation (und etwas Zahlentheorie) kann man zeigen:
2.40 Lemma. Ist die Folge M = (m1 , m2 . . .) berechenbar, so sind νM und (·)2 äquivalente Notationen.
2
Während bei gegebenem M die Umwandlung aus binärer Notation in die modulare Notation einfach ist, ist die
umgekehrte Übersetzung nicht ganz trivial: Naheliegend, aber von der Komplexität her völlig unbefriedigend wäre
eine vollständige Suche im Raum aller möglichen Zahlen 0 ≤ x < M (m) zu einem gegebenen Tupel (x 1 , . . . , xm )
von Resten. Stattdessen kann man auch verwenden, dass es wegen der Teilerfremdheit der m i Zahlen mi geben muß,
so dass 1 = mi mod mi und 0 = mi mod mj für j 6= i gilt. Dann ist einfach
X
x=
xi mi mod M (m)
Weitere Details zu dieser Notation ν M findet man z.B. in [Kn81]. Es sei nur angemerkt, dass es relativ aufwendig
ist, den Overflow-Fall bei der Berechnung zu testen oder etwa die Operationen ≤ auszuführen. Dennoch ist diese Art
der Notation Basis des bereits angesprochenen Schönhage-Strassen-Algorithmus.
2.7
Kompakte Notationen
Die Komplexitätsaussagen dieses Kapitels sind bisher immer auf den notierenden Namen, aber nicht auf die notierte
Zahl bezogen worden. So besagt die Aussage der Berechenbarkeit der Division in quadratischer Zeit „nur“, dass bei
einem Argument w mit Länge n die Laufzeit des Algorithmus durch O(n 2 ) beschränkt ist. Diese Aussage ist jedoch
in zweierlei Hinsicht unzureichend:
• Wenn wir die unäre Notation νu : IB →
mit νu (w) :=Länge(w) betrachten, so ergibt sich, dass die
Division hier sogar in linearer Zeit berechenbar ist: Die Umrechnung aus der unären Notation in Binärnotation
ist in linearer Zeit möglich, wie man am Beispiel des Zählens in Binärnotation erkennen kann. Die binären
Argumente kann man dividieren und das Ergebnis wieder umwandeln. Diese letzte Umwandlung ist linear in
der Länge der Ergebnisses der Umwandlung, und damit linear in der Länge n der ursprünglichen Argumente.
Insgesamt ergibt sich also ein Aufwand von O(n + (log 2 n)2 + n) = O(n).
Allgemein kann man zeigen, dass es zu jeder berechenbaren Funktion f :
gerade dieses f in linearer Zeit berechenbar macht (vgl.[We00]).
k
→
eine Notation gibt, die
bxc
• Betrachten wir andererseits die Berechnung der Funktion b zy c. Es ist naheliegend, diese aus zwei Divisionen mit Rest zusammenzusetzen. Wenn wir die Komplexität dieser zusammengesetzten Funktion abschätzen
wollen, können wir jedoch als Schranke für die Länge der Zwischenresultate nur die Rechenzeit der Zwischenberechnung annehmen (pro Rechenschritt wird maximal ein Zeichen ausgegeben). Damit erhalten wir jedoch
als Komplexitätsschranke nur O(O(n 2 )2 ) = O(n4 ) statt der zu erwartenden O(n2 )-Schranke
In beiden Fällen wäre es also hilfreich, wenn man die Komplexität einer Funktion nicht an den Namen eines Argumentes, sondern an das Argument selbst binden könnte. Für Komplexitätsbetrachtungen sollte man aber allen
Argumenten zusätzlich in sinnvoller Weise eine „Größe“ zuordnen können:
32
2 NATÜRLICHE / GANZE ZAHLEN
2.7 Kompakte Notationen
2.41 Definition. Gegeben seien Mengen X und Y mit Notationen ν X und νY sowie eine Abbildung G : X → .
Eine Funktion f : X Y ist bezüglich des Größenmaßes G in Zeit t : → auf einer Menge A ⊆ X (ν X , νY )berechenbar, wenn eine Turingmaschine M existiert, die f bzgl. der Notationen ν X und νY berechnet und für deren
Laufzeit TM (v) gilt:
(∀v : νX (v) ∈ A) G(νX (v)) = n ⇒ TM (v) ≤ t(n)
2
Es ist naheliegend, bei den natürlichen oder ganzen Zahlen als Größenmaß G(x) = log 2 |x| zu wählen.
Bei den bisher angegebenen Notationen tauchen hier neue Probleme auf: Es gibt plötzlich keine Komplexitätsschranken mehr! Durch die Möglichkeit führender Nullen in der Binärnotation gibt es zu jeder Zahl beliebig lange Namen.
Komplexitätsschranken sind jedoch jetzt an den Wert gebunden. Damit reicht keine endliche Zahl von Schritten
t(x) aus, um für eine gegebene Zahl x auch nur festzustellen, ob sie von Null verschieden ist. Wir werden daher
zusätzliche Bedingungen an die Notationen stellen, um sinnvoll Komplexität definieren zu können.
2.42 Definition. Eine Notation νX : IB∗ X heißt kompakt bezüglich des Größenmaßes G : X →
jedes n die Menge {v : G(νX (v)) = n} endlich ist.
, wenn für
2
Keine der bisher explizit angegebenen Notationen ist damit kompakt, da stets zu einem x unedlich viele Namen
existieren. Durch einfache Normierung können wir dies jedoch erreichen. So werden wir im folgenden fast ausschließlich normierte Fassung der Binär- oder der Signed-Magnitude-Notation verwenden:
2.43 Definition. Die Notation (·) : IB∗ →
und (v) := (v)2 .
Die Notation (·)
(v) := (v)sm .
sei definiert durch Def((·) ) = {v ∈ IB∗ | v beginnt nicht mit 0}
: IB∗ → sei definiert durch Def((·) ) = {v ∈ IB∗ | v beginnt nicht mit 00 oder 10} und
2
Da die obigen Notationen durch Einschränkung der Original-Notationen definiert sind, gilt trivialerweise (v) ≤
(v)2 und (v) ≤ (v)sm . Die anderen Richtungen (v) ≥ (v)2 und (v) ≥ (v)sm ergeben sich durch einfaches
Streichen führender Nullen und sind damit ebenfalls in linearer Zeit (bezogen auf die Argument-Länge) berechenbar.
Dies kann man als ein Normieren am Ende der Berechnung interpretieren.
Für Komplexitätsuntersuchungen wird nun auch wichtig, dass die Projektionsfunktionen so gewählt worden sind,
dass der Aufwand zur Extraktion einer Komponente nur von dieser selbst abhängt.
Offensichtlich sind diese Notationen nur für Hardware- sondern nur für Software-Implementierungen von Interesse.
Die GMP-Bibliothek verwendet eine analog normierte Signed-Magnitude-Notation (d.h. der signifikanteste „Limb“
darf nicht Null sein).
33
3 RECHNEN MIT RATIONALEN ZAHLEN
3
Rechnen mit rationalen Zahlen
Stichworte: Zähler/Nenner-Notation, Euklidischer Algorithmus, Normierung, Rundungen, untere Schranken für
den g.g.T.
Für numerische Berechnungen ist der Bereich der ganzen Zahlen unzureichend. Als Erweiterung bieten sich zunächst
die rationalen Zahlen an. Viele der Beispiele aus der Einleitung lassen sich auch im Prinzip bereits mit rationalen
Zahlen behandeln. Hier ist es naheliegend, eine rationale Zahl, die per Definition durch die Division zweier ganzer
Zahlen entsteht, durch die Aufzählung von Zähler und Nenner zu notieren.
Allerdings gibt es für jede rationale Zahl unendlich viele Brüche, die die gleiche Zahl repräsentieren. Daher normiert
man hier, indem man verlangt, dass Zähler und Nenner teilerfremd sind, d.h. dass ihr größter gemeinsamer Teiler
(g.g.T.) den Wert 1 hat:
3.1 Definition. Die Notation (·) : IB∗ sei definiert durch

 (z)
,
wenn (n) 6= 0 und g.g.T ((n) , (z) ) = 1
<z, n> :=
(n)

undefiniert, sonst
für alle <z, n> ∈ IB∗ .
2
In GMP findet man entsprechend die folgende Deklaration
typedef struct
{
__mpz_struct _mp_num;
__mpz_struct _mp_den;
} __mpq_struct;
...
typedef __mpq_struct mpq_t[1];
...
/* Allow direct user access to numerator and denominator of a mpq_t object.
#define mpq_numref(Q) (&((Q)->_mp_num))
#define mpq_denref(Q) (&((Q)->_mp_den))
*/
Durch Übertragung der herkömmlichen Bruchrechnung auf diese Notation ergibt sich ganz leicht:
3.2 Lemma. Die Grundrechenarten Addition, Subtraktion, Multiplikation und Division sind ((·) 2 , (·) )-berechenbar.
2
Beweis: Zum Nachweis der Berechenbarkeit braucht man sich nur die Regeln zur Bruchrechnung zu betrachten, wo
nur die Funktionen benötigt werden, die auf berechenbar sind:
z1
z2
z1 · n 2 + z 2 · n 1
+
=
n1 n2
n1 · n 2
,
z1
z2
z1 · n 2 − z 2 · n 1
−
=
n1 n2
n1 · n 2
z1 · z 2
z1 z2
z1 · n 2
z1 z2
·
=
,
/
=
n1 n2
n1 · n 2
n1 n2
n1 · z 2
Wir müssen nun nur noch dafür sorgen, dass die Zähler und Nenner jeweils teilerfremd sind.
2
Die Teilerfremdheit kann man mit Hilfe des Euklidischen Algorithmus erreichen, indem man zunächst den g.g.T.
bestimmt und dann Zähler und Nenner dadurch teilt:
3.3 Lemma. Die Funktion g.g.T. : 2
→ ist ((·)2 , (·) )-berechenbar in Zeit O(n · M(n))
34
2
3 RECHNEN MIT RATIONALEN ZAHLEN
Beweis: Zur Bestimmung von d := g.g.T.(x 0 , x1 ) (wobei wir o.B.d.A.annehmen können, dass x 0 > x1 > 0 gilt)
können wir die Iteration xj+1 := xj−1 mod xj verwenden, wobei wir solange iterieren, bis sich ein x i+1 = 0 ergibt.
Dann gilt d = xi .
Zum Nachweis der Korrektheit nutzen wir aus, dass x i+1 := xi−1 mod xi auch xi−1 = xi · yi + xi+1 für ein yi ∈
impliziert.
Da sicherlich xj > xj+1 ≥ 0 gilt, muß die Iteration nach endlich vielen Schritten i mit x i+1 = 0 aufhören. Dann
gilt jedoch xi−1 = xi · yi und per Induktion ist xi auch ein Teiler aller xj für j ≤ i, insbesondere also auch von x 0
und x1 , woraus xi ≤ d folgt. Andererseits ist d per Induktion auch ein Teiler aller x j für j ≤ i, woraus die auch die
Umkehrung d ≤ xi folgt.
Die Zahl i der notwendigen Iterationen können wir wie folgt abschätzen: Sicherlich ist stets x j−1 ≥ xj + xj−1 .
Damit muss x√i−j ≥ fj für die j-te Fibonacci-Zahl gelten, bei denen man zeigen
√ kann (vgl z.B. [Kn73]), dass sie
j
als Wert Φ / 5, gerundet zur nächsten natürlichen Zahl, haben, wobei Φ = ( 5 + 1)/2 ≈ 1.618. Daraus ergibt
sich sofort i = O(log x1 ), d.h. i ist linear in der Zahl der Stellen von x 1 . In jeder Iteration brauchen wir zudem eine
Division mit Rest bei Zahlen, die höchstens n = log max(x 0 , x1 ) Stellen haben.
2
Die obige Analyse ist eine Worst-Case-Analyse, man könnte also bei einer Implementierung auf ein besseres mittleres Verhalten hoffen. Dies trifft jedoch nicht zu. Bei x i+1 = xi−1 mod xi liegt der Wert xi+1 ziemlich gut gleichverteilt im Intervall [0, xi ], was dafür sorgt, dass die asymptotische Wachstum der Fibonacci-Zahlen gut eingehalten
wird. Zudem ist mit hoher Wahrscheinlichkeit der g.g.T. klein, so dass auch im Allgemeinen fast die Maximalzahl
an Iterationen notwendig ist:
3.4 Lemma.(Dirichlet, 1849)
Wählt man zu gegebenem n zwei Zahlen x und y gleichverteilt aus {1, . . . , n}, so konvergiert die Wahrscheinlichkeit
für g.g.T.(x, y) = 1 mit wachsendem n gegen 6/π 2 ≈ 0.60793.
2
Einschub:
g.g.T.=1 mit 60%
Wie man leicht nachrechnet, ist g.g.T.(x, y) ≤ 10 sogar mit einer Wahrscheinlichkeit von rund 95%.
Wegen der mehr als quadratischen Laufzeit ist die Normierung der Resultate bei der rationalen Arithmetik ein Faktor,
der die Komplexität wesentlich beeinflußt. Wichtig ist dies bei der Implementierung: Nehmen wir an, das wir zwei
rationale Zahlen mit ungefähr gleicher Anzahl n von Stellen bei Zähler und Nenner haben. Bei der Multiplikation
2
mit Resultat nz11 ·z
·n2 kostet der g.g.T. von z1 · z2 und n1 · n2 etwa c · 2n · M(2n). Es ist jedoch auch möglich, bereits
vor der Ausführung der Multiplikationen bei den Paaren z 1 , n2 und z2 , n1 zu kürzen, wozu man dann zwei g.g.T.Berechnungen mit n Stellen ausführen muß. Diese würden in etwa einen Aufwand von 2 · c · n · M(n) erfordern und
wären damit um etwa 50% schneller. Ähnliches gilt für die anderen arithmetischen Operationen.
Aufbauend auf den rationalen Zahlentyp aus GMP enthält auch die iRRAM-Bibliothek [Mu01] rationale Zahlen.
Wir werden diese Bibliothek im folgenden für Beispiele verwenden. Für das Beispiel zur schwierigen Grenzwertbestimmung von Jean-Michel Muller erhalten wir hier z.B. folgendes Programm:
#include "iRRAM.h"
/* Compute iteration by J.M.Muller */
void compute(){
int test,count;
35
3 RECHNEN MIT RATIONALEN ZAHLEN
rprintf("JMM-example: c=111-(1130-3000/a)/b");
rscanf("%d",&test);
rprintf("how many values: ");
rscanf("%d",&count);
RATIONAL a=RATIONAL(11)/2, b=RATIONAL(61)/11, c;
for (long i=1;i<=count;i++ ) {
rwrite(REAL(a),18); rprintf(" %d\n",i);
c=111-(1130-3000/a)/b;
a=b; b=c;
}
}
Zur Bestimmung von 10000 Werten benötigt das Programm auf einem Athlon mit 900 MHz ca 1min und hat einen
vernachlässigbaren Speicherbedarf. Ein großer Teil der Rechenzeit wird dabei sogar für die Ausgaben verwendet.
Wie in der Einleitung bereits angegeben, gilt hier:
an =
6n+1 + 5n+1
6n + 5 n
Daraus ergibt sich, dass die Größe von Zähler und Nenner jeweils linear in n ist. Daraus ergibt sich ein Aufwand zur
Bestimmung von an , der in der Größenordnung von n g.g.T-Bestimmungen der Länge n, also in O(n 2 · M(n)) liegt.
Dies ist jedoch nicht der Normalfall: Da der g.g.T. sehr häufig klein ist, ergibt sich üblicherweise, dass der Wert von
Zähler und Nenner nach einer rationalen Rechenoperation in der Größenordung des Produktes der beteiligten Zähler
bzw. Nenner liegt. Mit anderen Worten: Pro Operation verdoppelt sich ungefähr die Platzbedarf der Zahlen, d.h.
nach n Operationen in Folge haben wir einen Platzbedarf in der Größe O(2 n ) und für die jeweils nächste Operation
einen Zeitbedarf von O(2n · M(2n )). Dieses Verhalten findet man zum Beispiel bei der logistischen Funktion aus
der Einleitung. Der Kern der Berechnung kann hier wie folgt implementiert werden:
RATIONAL x = RATIONAL(1)/RATIONAL(2);
RATIONAL c = RATIONAL(375)/RATIONAL(100);
for ( int i=0; i<=count; i++ ) {
if ( (i<100) || (i%10)==0 ) {
rwrite(REAL(x),18); rprintf(" %d\n",i);
}
x= c*x*(RATIONAL(1)-x);
}
Für nur 25 Iterationsschritte benötigt das Programm auf einem Athlon mit 900 MHz bereits rund 6 min und rund 200
MB an Hauptspeicher! Die exakte Berechnung von x 30 ist damit in der Praxis bereits unmöglich.
Rationale Arithmetik ist also nur sehr eingeschränkt nutzbar, wenn der zu implementierende Algorithmus vorher
gründlich auf seien Eignung untersucht wurde. Um dem Problem der explositionsartig wachsenden Werte zu begegnen, wurde vorgeschlagen, Rundungsalgorithmen zu verwenden, d.h. eine Zahl z/n mit langen Komponenten durch
eine Zahl z 0 /n0 mit wesentlich kleineren Werten z 0 und n0 zu ersetzen, für die aber immerhin noch z 0 /n0 ≈ z/n gilt.
Damit geht jedoch der Hauptgrund für die Verwendung rationaler Arithmetik, die Exaktheit der Werte, unwiederbringlich verloren. Dann ist es sinnvoller, Fließkommaberechnungen mit verifizierbarer Genauigkeit zu verwenden.
36
4 FLIESSKOMMA-ARITHMETIK
4
Fließkomma-Arithmetik
Stichworte: IEEE 754/854 Fließkomma-Standard, gerichtetes Runden, MPFR, Reduktionsmethoden in der Arithmetik,
Die Fließkomma-Notation entspricht der sicher bereits bekannten halblogarithmischen Schreibweise von Zahlen:
4.1 Definition. Eine Notation (·)hl : Σ∗r → Dr der r-adischen Zahlen Dr = {m · r e | m, e ∈ } sei definiert durch
<vm , ve , m, e>hl := (−1)vm (m)r · r (−1)
ve (e)
r
für alle <vm , ve , m, e> ∈ Σ∗r .
2
Die hier definierten r-adischen Zahlen sind „nur“ eine relativ kleine Teilmenge der rationalen Zahlen. Die Zahlmenge
ist sogar, ähnlich wie die ganzen Zahlen, nicht unter Division abgeschlossen: Eine rationale Zahl z/n mit 0 < z < n
besitzt nur dann eine endliche r-adische Entwicklung, wenn n selbst eine Potenz von r ist. Als Konsequenz werden
die Berechnungen hier i.A. nicht exakt, sondern fast immer nur von approximativer Natur sein.
Algorithmen werden sich im Wesentlichen auf folgende Eigenschaften von Produkten der Form m i · r ei mit ganzen
Zahlen mi , ei stützen:
• Sei e := min{e1 , e2 }. Dann ist
(m1 · r e1 ) + (m2 · r e2 ) = (m1 · r e1 −e + m2 · r e2 −e ) · r e
Einer der Werte ei −e ist nach Konstruktion = 0, der andere ist ≥ 0. Zur Addition (und analog zur Subtraktion)
reichen also ganzzahlige Additionen/Subtraktionen und Shifts aus.
• Stets gilt
(m1 · r e1 ) · (m2 · r e2 ) = (m1 · m2 ) · r e1 +e2
Für eine Multiplikation brauchen wir also eine ganzzahlige Multiplikation und wiederum ganzzahlige Additionen.
• Die Division ist komplexer, da wir kein exaktes Ergebnis erwarten können. Für beliebiges e ∈ ist
(m1 · r e1 ) / (m2 · r e2 ) = bm1 · r e /m2 c · r e1 −e2 −e + (m1 · r e mod m2 ) · r e1 −e2 −e
Als Ergebnis der Division wird nun bm 1 · r e /m2 c · r e1 −e2 −e benutzt, wobei wir über die Wahl von e den
entstehenden Fehler, der durch m2 · r e1 −e2 −e beschränkt ist, im Prinzip beliebig klein halten könnten.
Wir werden uns in diesem Abschnitt zunächst den in heutigen CPUs benutzten Fließkomma-Standard „IEEE 7541985 Standard for Binary Floating-Point Arithmetic“, bei dem im Allgemeinen nur 4 oder 8 Byte zur Repräsentation
von Zahlen verwendet werden. Der Standard wurde später noch einmal als „IEEE 854-1990 Standard for RadixIndependent Floating-Point Arithmetic“ aktualisiert. Als Basis verwenden wir hier [Pa00]. In diesem Standard wird
die obige Notation in leicht modifizierter Form angewendet: Eine Bitgruppe w fester Länge 1 + l e + lm wird in drei
Komponenten mit ebenfalls fester Länge zerlegt, w = v ◦ e ◦ m:
• ein Vorzeichen-Bit v
• eine Bit-Gruppe e mit Länge le zur Notation des Exponenten und
• eine Bit-Gruppe m mit Länge lm zur Notation der Mantisse
37
4 FLIESSKOMMA-ARITHMETIK
Zwei Besonderheiten sind nun zu beachten: Der Exponent wird über eine um einen festen Wert bias „verschobene“
Binärnotation festgelegt. Der Wert der Mantisse wird so interpretiert, als ob sie die Form ‘1.m’ hätte, wobei diese
führende 1 nicht aufgeschrieben wird. m wird also als ein Wert aus dem halboffenen Intervall [1, 2) interpretiert,
indem man eine Multiplikation des binären Wertes (1m) 2 mit 2−lm vornimmt. Damit erhält man explizit:
(v e m)IEEE = (−1)v · (1m)2 · 2(e)2 −bias−lm
Dabei sind zunächst explizit die Fälle mit e = 0 . . . 0 und e = 1 . . . 1 ausgenommen. Der Fall e = 1 . . . 1 wird
benutzt, um zusätzliche spezielle Werte zu kodieren: ±∞ und NaN (not a number). Dabei wird NaN benutzt, um
weiterzugeben, dass ein Operationsergebnis nicht definiert war, etwas bei der Division 0/0. Als Konsequnez aus
der Existenz der NaNs müssen bei Vergleichen von Zahlen auch 4 mögliche Resultate zur Verfügung stehen: kleiner,
gleich, größer und zudem ungeordnet, wobei der letztere Wert bei jedem Vergleich mit einer NaN entsteht. Dargestellt
wird NaN durch Bitfolgen der Art v 1 . . . 1 m mit m 6= 0 . . . 0.
Die Werte ±∞ entstehen bei Division x/0 mit x 6= 0 oder wenn ein Overflow vorliegt. Sie haben dabei die Darstellung v 1 . . . 1 0 . . . 0.
Im Fall e = 0 . . . 0 werden „denormalisierte“ Zahlen benutzt, d.h. die führende 1 bei der Mantisse darf dann fehlen.
Es ergeben sich als Werte hier also
(v 0 . . . 0 m)IEEE = (−1)v · (m)2 · 2−bias−lm
Als Spezialfälle erhält man bei v ◦ 0 . . . 0 ◦ 0 . . . 0 eine positive oder negative Null!
Die folgende Tabelle zählt einige der bestimmenden Parameter sowie der Besonderheiten des ANSI/IEEE-Standards
auf:
32 bit / Single
64 bit / Double
Eigenschaft
Mantissenlänge lm in Bits
23 (+1 verstecktes Bit)
52 (+1 verstecktes Bit)
Wertebereich normalisierter Mantissen [1 , 2 − 2 −23 ]
[1 , 2 − 2−52 ]
Exponentenlänge le in Bits
8
11
127
1023
Bias des Exponenten
kleinste positive normalisierte Zahl
2 −126 ≈ 1.2 · 10−38
2−1022 ≈ 2.2 · 10−308
2 128 − 2104 ≈ 2128 ≈ 3.4 · 1038 21024 − 2971 ≈ 21024 ≈ 1.8 · 10308
größte positive normalisierte Zahl
kleinste positive denormalisierte Zahl
2 −150 ≈ 7 · 10−46
2−1075 ≈ 4.94 · 10−324
Wegen der festen Mantissenlänge ist bei Fließkomma-Operationen in der Regel das Ergebnis nicht exakt, selbst bei
einfachen Additionen. Daher muss normalerweise nach jeder Operation gerundet werden. Es gibt dabei mehrere
Möglichkeiten: Default-Modus ist das Runden zum nächsten Wert in der Form (round to nearest even), wo auch
explizit festgelegt wird, wohin gerundet werden soll, wenn der zwei gleich gute Möglichkeiten zum Runden zur
Verfügung stehen. Daneben sieht IEEE verschiedene Modi für gerichtetes Runden (directed rounding) vor.
Die Rundungsverfahren kann man am einfachsten unterscheiden, wenn man sich ansieht, wie ein Bitmuster ± . . . x 1 x0 •
x−1 x−2 . . . zu einer ganzen Zahl gerundet wird:
• Round to Nearest Even (rtne)
Runden zu . . . x1 x0
Runden zu . . . x1 x0
Runden zu . . . x1 x0 + 1,
Runden zu . . . x1 x0 − 1,
38
x−1 = 0
x−1 x−2 . . . = 100 . . . , x0 = 0
sonst, bei positiver Zahl
sonst, bei negativer Zahl
4 FLIESSKOMMA-ARITHMETIK
Hierdurch wird erreicht, dass der Rundungsoperator symmetrisch arbeitet:
rtne(x)
4
3
2
1
−4
−3
−2
−1
0
1
2
3
4
1
2
3
4
x
−1
−2
−3
−4
• Round to Zero (rtz)
Hier werden einfach die Bits x−1 x−2 . . . gestrichen:
rtz(x)
4
3
2
1
−4
−3
−2
−1
0
x
−1
−2
−3
−4
• Round to +∞ (up)
Für negative Argumente entspricht dies dem Round to Zero, bei positiven Argumenten wird aufgerundet,
sobald x−1 x−2 . . . nicht nur aus Nullen besteht:
up(x)
4
3
2
1
−4
−3
−2
−1
0
−1
−2
−3
−4
39
1
2
3
4
x
4 FLIESSKOMMA-ARITHMETIK
• Round to −∞ (down)
Dies ist da Analogon zu Round to +∞, aber mit vertauschten Rollen der Vorzeichen:
down(x)
4
3
2
1
−4
−3
−2
−1
0
1
2
3
4
x
−1
−2
−3
−4
Während Round to Nearest Even versucht, den mittleren Fehler beim Runden gleichmäßig zu verteilen, so dass sich
bei längeren Berechnungen die Fehler ausmitteln könnten, haben die gerichteten Rundungen den Vorteil, dass man
genau weiss, in welcher Richtung der Fehler liegt: Mit a = x + y, gerundet gegen −∞, und b = x + y, gerundet
gegen +∞, liegt das exakte Ergebnis von x + y im Intervall [a, b].
Da sich über eine Exception überprüfen lässt, ob eine Rundung stattgefunden hat, reicht es einen der Werte a oder b
zu bestimmen: Wurde nicht gerundet, so ist a = b, ansonsten differieren a und b um das letzte Bit der Mantisse.
Der Fehler, der durch eine Rundung entsteht, hat als Größenordnung maximal die Stelligkeit des letzten Bits des
Resultates. Die durch die (unvermeidbaren) Rundungen entstehenden Fehler führen dazu, dass die üblichen Rechenregeln für Fließkommazahlen nicht mehr gültig sind: Bezeichnen wir mit ⊕, , ⊗ und die Varianten der
arithmetischen Grundoperationen, die bei Fließkomma-Ausführung mit anschließender Rundung entstehen, so gilt
im Allgemeinen
(x ⊕ y) ⊕ z 6= x ⊕ (y ⊕ z)
Gerade diese Assoziativität der Addition
P wird jedoch in vielen mathematischen Formeln an zentraler Stelle ausgenutzt, selbst bei einfachen Summen 1≤k≤n ak ist es für dem Computer nicht gleichgültig, in welcher Reihenfolge
man aufsummiert!
Auch das Distributionsgesetz ist nicht mehr gültig:
(x ⊕ y) ⊗ z 6= (x ⊗ z) ⊕ (y ⊗ z)
Wir greifen noch einmal das Beispiel des linearen Gleichungssystems aus der Einleitung auf:
1
64919121 −159018721
und b = (bi ) =
A = (aij ) =
0
41869520.5 −102558961
mit Lösung
x1 = a22 /(a11 · a22 − a12 · a21 )
x2 = −a21 /(a11 · a22 − a12 · a21 )
Bei den Multiplikationen a11 · a22 und a12 · a21 entstehen zwei fast gleiche Werte: -6658037598793281 und
-.6658037598793280.5. Die Differenz der zwei Werte ist also 0.5. Allerdings wird der zweite Wert bei 64Bit-Fließkommazahlen zusätzlich um 0.5 nach unten abgerundet, so dass als Differenz im Rechner 1 entsteht. Daraus
ergeben sich die in der Einleitung genannten falschen Werte.
Der Standard verlangt, dass die implementierten Funktionen den obigen Rundungen entsprechend korrekt sein müssen. Problematisch ist jedoch, dass diese Rundungen laut Standard auch für weitere Funktionen wie etwa Sinus,
40
4 FLIESSKOMMA-ARITHMETIK
Cosinus etc. korrekt erfüllt sein sollen. Bei heutiger Hardware ist dies nicht immer gewährleistet. Es kann hier sogar
vorkommen, dass noch nicht einmal die Monotonie von Funktionen erhalten bleibt!
Eine aktuelle Software-Lösung für Fließkomma-Arithmetik ist z.B MPFR [Zi00], www.loria.fr/projets/mpfr/, für
„Multiprecision Floating-Point Arithmetic with Exact Rounding“, die eine Erweiterung der GMP-Bibliothek darstellt.
Die Bibliothek verbindet eine Fließkomma-Notation mit variabler Länge mit den 4 IEEE-Rundungsmodi. Dabei
kann für Fließkomma-Variablen die Länge (in Bits) der Mantisse beliebig vorgegeben werden, Als Exponent wird
31
eine 32-Bit-LongInt verwendet, d.h es können hier Zahlen im Bereich von ±2 ±2 ≈ ±10±646457000 mit einer
Mantissenlänge bis zu rund 500 MByte notiert werden.
#include "gmp.h"
#include "mpfr.h"
main(int argc, char *argv[])
{
int prec;
mpfr_t x, z;
...
mpfr_init2(x, prec);
mpfr_set_d(x, 3.0, GMP_RNDN);
mpfr_log(z, x, GMP_RNDN);
...
}
Wir werden später noch zeigen, wie man mit dieser Bibliothek numerische Verfahren implementieren kann. Eine
für die Rundungsmodi wichtige Routine sei hier noch erwähnt: Sie macht auch erkenntlich, warum die HardwareImplementierung der korrekten Rundungen sich sehr schwer gestalten kann:
int mpfr_can_round (mpfr_t b, mp_exp_t err,
mp_rnd_t rnd1, mp_rnd_t rnd2,
mp_prec_t prec)
Gegeben ist hier im Allgemeinen eine Variable b, die eine Approximation einer (nicht exakt bekannten) Zahl z sei.
Durch eine genaue Fehler-Analyse sei bekannt, dass dabei b von z aus in der (Rundungs-)Richtung rnd1 mit einem
Abstand von höchstens 2E(b)−err liegt, wobei E(b) der Exponent von b ist (wieder bezogen auf das höchstwertige
Bit). Das Resultat der Funktion gibt nun an, ob eine Rundung auf Genauigkeit prec in Richtung rnd2 möglich ist
oder nicht.
Beispiel: Betrachte b=0.100111100...
ˆ
· 2 0 , rnd1=rnd2=GMP_RNDZ und prec = 4. Hier ist E(b) = −1. Bei
err = 5 ergäbe sich z ∈ (0.10011110..., 0.101000100..), weshalb eine Rundung hier nicht möglich wäre.
Bei err = 7 erhalten wir z ∈ (0.100111100..., 0.100111110..), was eine korrrekte Rundung zu 0.1001 · 2 0 erlaubt.
Ziel ist i.A. die Berechnung von z = f (x), wobei das Ergebnis in einer Variablen a gespeichert werden soll und
das Argument eine Fließkommazahl ist, bei der der Rundungstyp rnd2 sowie die Genauigkeit prec der Variablen
a vorgegeben sind. Bei einem Programm zur Berechnung von f , dass als Ergebnis b liefert, müssen also nun alle
Berechnungen so genau durchgeführt werden, dass der Fehler am Ende so klein wird, dass auch die Rundung möglich
wird. Dazu ist es notwendig, dass wir (je nach Rundungstyp) im Bereich zwischen prec und err einen Wechsel
von 0 auf 1 oder 1 auf 0 haben oder aber dass wir wissen, dass die Zahl periodisch mit 0 oder 1 endet.
Bei Addition, Subtraktion und Multiplikation ist dies offensichtlich möglich: Hier können wir durch Wahl einer
ausreichend hohen Genauigkeit sogar exakte Ergebnisse erhalten (d.h. mit einer Periode 0). Bei der Division erhalten
wir auf jeden Fall ein periodisches Resultat, wo die Rundung auch leicht möglich ist.
41
4 FLIESSKOMMA-ARITHMETIK
Ein Beispiel für die Rundugns-Problematik bei IEEE-754-Zahlen findet sich in [MJM97]. Die Exponential-Funktion
ist an der folgenden Stelle nur schwer exakt rundbar:
x = (0.1101011001100111110111110010011010110100111011110000) 2
wo sich für ex der Wert
10.010011111000010111001001011110000011110111001110000 01111
11111111111111111111111111111111111111111111111111101...
ergibt. Man muß also hier zu dem 53 Bits des Ergebnisses noch zusätzliche 55 Bits berechnen, um korrekt runden zu
können!
Die am Anfang des Kapitels angegebenen Basis-Algorithmen für exakte Fließkomma-Arithmetik haben noch den
Nachteil, dass bei der direkten Umsetzung insbesondere bei der Multiplikation die Länge der Mantisse sehr schnell
wächst und damit die gleiche Problematik wie bei der rationalen Arithmetik entsteht. Daher wird i.A. gerundet, selbst
wenn exakte Resultate möglich wären.
Festgelegt werden muß jedoch, wie genau das Ergebnis zu berechnen ist. Dazu gibt es i.W. zwei Möglichkeiten:
Rechnen mit absoluter oder mit relativer Genauigkeit.
Relative Genauigkeit entspricht i.W. der Hardware-Fließkomma-Arithmetik, da hier der Speicherplatz für das Ergebnis im Voraus festgelegt wird. Der Sinn der oben definierten Rundungs-Modi ist zudem, dass der Unterschied
zwischen dem exakten Ergebnis und dem Resultat der implementierten Fließkommaoperation nur eine Änderung im
letzten Bit der Mantisse (evtl. mit Carry) betragen darf. Durch die Normierung der Werte (führendes Bit der Mantisse 6= 0) ergibt sich, dass wir hier Fließkommaoperationen ‘ n ’ als Operationen mit folgender Fehler-Abschätzung
interpretieren können. Dabei sei ‘ n ’ die ‘◦’ entsprechende Fließkomma-Operation.
Der für das Resultat a n
a n b − a ◦ b
≤ 2−n
a nb
b notwendige Speicher ist hier von a ◦ b unabhängig und wird direkt durch n festgelegt.
Prinzipiell kann n hier alle Werte aus
annehmen. Problematisch ist hier, dass durch die relative Genauigkeit
Abschätzungen erschwert werden, wenn das Resultat den Wert Null hat oder sehr klein ist.
Als Alternative kann man den Fehler bei Operationen über absolute Genauigkeit formulieren:
|a p
b − a ◦ b| ≤ 2p
Hier kann p nun eine beliebige positive oder negative ganze Zahl sein. Hier hängt der zur Bestimmung eines entsprechenden Resultates notwendige Speicher außer von p auch direkt von a ◦ b ab. Was zunächst wie ein Nachteil
aussieht, erlaubt jedoch einerseits leichter Fehlerabschätzungen und verlangt zudem von Anwender nicht, dass er sich
Gedanken über die Platznutzung macht. Dieser Zugang wird i.A. in komplexitäts-orientierten Arbeiten über exakte
Arithmetik auf wie [We00] oder in der iRRAM [Mu01] verwendet, da sich dort die Problematik der undefinierten
relativen Genauigkeit deutlich wiederspiegelt.
Wichtig sind diese Fehlerabschätzungen, wenn wir komplexere Ausdrücke oder ganze Berechnungen analysieren
wollen: Als Beispiel betrachten wir die Auswertung des Ausdrucks a + b + c + d durch e := (a p b) p (c p d)
unter Verwendung der Dreiecksungleichung:
|e − (a + b + c + d)| ≤ |e − (a p
≤ 2 + |(a ≤ 3·2
p
p
b) + (c p
p
d)| + |(a b) − (a + b)| + |(c 42
p
b) + (c p
d) − (c + d)|
p
d) − (a + b + c + d)|
4 FLIESSKOMMA-ARITHMETIK
Wenn wir nun das Ergebnis mit einem Fehler von 2 p brauchen, reicht es hier, die Zwischenberechnungen genauer
auszuwerten: Bei e0 := (a p−2 b) p−1 (c p−2 d) erhalten wir analog |e0 − (a + b + c + d)| ≤ 2p
Als komplexeres Beispiel betrachten wir nun die Bestimmung des Kehrwertes 1/a über die Iterationsformel Φ(x) =
2x−ax2 : Zunächst sei a ∈ [1, 2), was genau dem Wert der Mantisse entspricht, wenn wir den Exponenten ignorieren.
Bei ε := 1/a − x erhalten wir dann
Φ(x) = 2(1/a − ε) − a(1/a − ε)2
= 2(1/a − ε) − a(1/a2 − 2ε/a + ε)2
= 1/a − ε2
Bei einer (exakten) Iteration xi+1 = Φ(xi ) wird der Fehler also i.W. quadriert. Starten wir also z.B. mit einem Fehler
i
von ε0 , so erhalten wir schließlich εi = (a · ε0 )2 /a. Bei 1 ≤ |a| < 2 und mit x0 := 0.75 ergibt sich ε0 = 2−2 und
i
damit εi < 2−2 .
Verwendet man statt eines festen x0 eine Tabelle mit z.B. 1024 Werten, die aus den ersten Bits der Mantisse von a
bestimmt werden, ergibt sich sogar ε 0 ≤ 2−11 und damit ε1 ≤ 2−21 , ε2 ≤ 2−41 und schließlich ε3 ≤ 2−81 . Damit
reichen bereits 3 Iterationen, um einen Kehrwert mit einer Genauigkeit zu bestimmen, die für das 8-Byte-IEEEFließkommaformat reichten sollte.
Da wir statt einer exakten Iteration stets in der Praxies auch die Rundungsfehler betrachten müssen, müssen wir
jedoch hier auch noch bedenken, dass der Fehler ε noch durch die unexakte Berechnung von f steigen wird. Wählen
wir aber zum Beispiel x0 und p0 < 0 mit ε0 < 2p0 −2 und pi = 2 · pi−1 , so ergibt sich iterativ εi ≤ 2pi −2 , wenn wir
jede Funktionsauswertung xi = Φ(pi −3) (xi−1 ) mit einem Rechenfehler von maximal 2 pi −3 ausführen:
εi ≤ aε2i−1 + 2pi −3 ≤ 2 · 22pi−1 −4 + 2pi −3 = 2pi −2
Hier ergibt sich also genauer bei einem Startfehler von 2 −11 und damit p0 = −9 nach 3 Iterationen p3 = −72 und
damit ε3 ≤ 2−74 , was aber immer noch ausreichend genau ist.
Um jetzt Fließkomma-Algorithmen genauer angeben und ihre Komplexität zuverlässig abschatzen zu könne, werden
wir von nun an die folgende Fassung der halblogarithmischen Notation der dyadischen Zahlen D verwenden (bei
fester Basis r = 2). Zur Zeit exitiert jedoch noch keine Implementierung, bei der in ähnlicher Form auch variabel
lange Exponenten verwendet werden. Wir werden dennoch den Exponenten bei den Komplexitätsabschätzungen
immer explizit beachten.
4.2 Definition. Die Notation (·)D : IB∗ → D der dyadischen Zahlen D = {m · 2e | m, e ∈ } sei definiert durch
<m, e>D := (m) · 21−lg(m) · 2(e)
für alle <m, e> ∈ IB∗ .
2
Dabei entspricht (e) dem Exponenten der notierten Zahl und (m) · 22−lg(m) ihrer Mantisse. Da bei (·) das erste
Bit das Vorzeichen angibt und das zweite Bit (falls vorhanden) den Wert 1 haben muß, gilt damit analog zu den
IEEE-Formaten
(m) · 21−lg(m) ∈ [0.5, 1)
Durch diese Normierung der Mantisse brauchen wir bei Komplexitätsbetrachtungen im Allgemeinen die Länge der
Mantissen nicht zu betrachten, wie wir später noch sehen werden!
Die arithmetischen Operationen p betrachten wird im Folgenden dabei als dreistellig, wobei die ersten zwei Parameter die Fließkomma-Argumente sind und der dritte Parameter die gewünschte absolute Genauigkeit im obigen
Sinne angibt.
43
4 FLIESSKOMMA-ARITHMETIK
4.3 Lemma. Es gibt (((·)D , (·)D , (·) ), (·)D )-berechenbare Funktionen , , : D × D × → D und eine
(((·)D , (·) ), (·)D )-berechenbare Funktion INV: D × D, Def (INV)= D\{0} × mit folgenden Eigenschaften
für alle (x, y, p) bzw. (x, p) aus dem jeweiligen Definitionsbereich:
| (x + y) − x | (x − y) − x | (x · y) − x p
p
p
y | ≤ 2p
y | ≤ 2p
y | ≤ 2p
| (1/x) − INV(x, p) | ≤ 2p
2
Anmerkung: Durch die Kombination von Kehrwert und Multiplikation erhält man natürlich auch die Division mit
der Eigenschaft |(x/y) − x p y| ≤ 2p für alle x, y ∈ D, y 6= 0.
Beweis: Wie zu Beginn des Kapitel bereits angesprochen, werden wir die elementaren Operationen auf D 2 durch
Transformation in die ganzen Zahlen durchführen. Dazu sind folgende Funktionen hilfreich:
SIGN: D → , SIZE:D\{0} → , NORM: × → D sowie DENORM:D × → mit folgenden Eigenschaften
für x ∈ D und m, n ∈ :

 1 , x>0
SIGN(x) =
0 , x=0

−1 , x < 0
SIZE(x) = min{i ∈ | |x| < 2i }
NORM(m, n) = m · 2n (∈ D)
DENORM(x, n) = trunc(x · 2−n ) (∈ )
Dabei ist trunc die Rundung in Richtung auf die Null, d.h.
max{i ∈ | i ≤ x} ,
trunc(x) =
min{i ∈ | i ≥ x} ,
falls x ≥ 0
falls x < 0
Bevor wir auf die Implementierung dieser Funktionen eingehen, werden wir sie zunächst einmal bei der Arithmetik
auf D anwenden. Wir betrachten zunächst den folgenden Algorithmus, der in einer an PASCAL oder MODULA
angelehnten Pseudosprache formuliert ist:
FUNCTION ADD (X,Y: DYADIC; P: WHOLE): DYADIC;
VAR A,B,C: WHOLE; Z:DYADIC
BEGIN
A:=DENORM (X, P-1);
B:=DENORM (Y, P-1);
C:=A+B;
Z:= NORM(C, P-1);
RETURN Z:
END;
Sind x, y, p, a, b, c, z die Werte der entsprechenden Variablen (am Ende der Funktion), so ergibt sich:
|x + y − z| = x + y − c · 2p−1 = x + y − (a + b) · 2p−1 = 2p−1 · x · 21−p − a + y · 21−p − b
≤ 2p−1 · 2 = 2p
Damit berechnet die obige Funktion wie erwartet die Funktion . Ersetzen wir C:=A+B durch C:=A-B, so erhalten
wir eine Implementierung der Funktion . Bei der Multiplikation muss die Denormalisierung sorgfältiger bedacht
werden:
44
4 FLIESSKOMMA-ARITHMETIK
FUNCTION MULT (X,Y: DYADIC; P: WHOLE): DYADIC;
VAR A,B,C,E,EX,EY: WHOLE; Z:DYADIC
BEGIN
IF SIGN (X)=0 OR SIGN (Y)=0 THEN
Z:=0
ELSIF P>SIZE (X)+ SIZE (Y) THEN
Z:=0
ELSE
EX:=P-SIZE(Y)-2;
EY:=P-SIZE(X)-2;
E:=EX+EY
A:=DENORM (X, EX);
B:=DENORM (Y, EY);
C:=A*B;
Z:= NORM(C, E);
END;
RETURN Z:
END;
Es seien wiederum x, y, p, a, b, c, e, z, e x , ey die Werte der entsprechenden Variablen. Damit ergibt sich bei x = 0
oder y = 0 sofort z = 0 = x · y. Im folgenden sei daher x 6= 0 und x 6= 0. Wir setzen s x := SIZE(x) bzw.
sy := SIZE(y), also ex = p − sy − 2, ey = p − sx − 2 und e = ex + ey .
Ist nun p > sx + sy , so ist |x · y| ≤ 2p , d.h. z := 0 hat einen ausreichend kleinen Abstand zu dem Produkt x · y.
Ansonsten ist p ≤ sx + sy und wir erhalten
|x · y − z| = |x · y − ab2e | = 2e · x2−ex · y2−ey − ab
= 2e · | x2−ex · (y2−ey − b) + (x2−ex − a)(b − y2−ey )
+(x2−ex − a) · y −ey |
≤ 2e · (2sx −ex · 1 + 1 · 1 + 1 · 2sy −ey )
= 2ey +sx + 2ex +ey + 2ex +sy
= 2p−2 + 2p−sy −2+p−sx −2 + 2p−2
≤ 2p−2 + 2p−4 + 2p−2 < 2p
Als letztes Beispiel betrachten wir die Bestimmung des Kehrwertes 1/x einer Zahl x 6= 0 durch Rückführung auf
die ganzzahlige Division:
FUNCTION INV (X: DYADIC; P: WHOLE): DYADIC;
VAR A,C,EX,EZ: WHOLE; Z:DYADIC
BEGIN
IF SIGN (X)=0 THEN
ERROR („Division durch 0“ )
ELSIF P>-SIZE (X) THEN
Z:=0
ELSE
EX:=P + 2* SIZE(X) - 5;
EZ:=EX - SIZE(X) + P - 1;
A:=DENORM(X,EX);
C:=2**(-EZ-EX) DIV A
Z:= NORM(C,EZ);
END;
RETURN Z
END.
45
4 FLIESSKOMMA-ARITHMETIK
Wiederum seien x, z, p, a, c, ex , ez die Werte der entsprechenden Variablen. Wir können o.B.d.A. annehmen, daß
x 6= 0 gilt. Es sei wieder sx := SIZE(x).
Gilt nun p > −sx , so ist (ganze Zahlen!) p ≥ 1 − sx , mit 2sx −1 <| x |≤ 2sx erhalten wir x1 ≤ 21−sx ≤ 2p , d.h.
z = 0 ist eine ausreichend genaue Näherung. Im folgenden sei daher p ≤ −s x . Dann erhalten wir
1
− z
x
≤
1
− c · 2 ez x
−e
−e
1 2 z x e 2−ez −ex e
ez
z
z
−
· 2 + · 2 − c2 x
a
a
−ez −ex
1
1 −ex ez 2
2
− c
x2−ex − a + 2 a
−ex
−ex 1
+ 2ez |a|
(x2
−
a)
2
a2
1
2−ex 2 + 2ez |a|
a
2
1
2−ex
+ 2ez · 2sx −ex
23−p−sx
=
2−p−2sx +5−6+2p+2sx + 2ex −sx +p−1+sx −ex = 2p
=
≤
=
(1)
≤
≤
(1)
(1) ergibt sich dabei einerseits mit dem Mittelwertsatz der Analysis ( x1 − y1 = − ξ12 (x − y), ξ zwischen x und y) und
mit |a| ≤ |x2−ex |, und andererseits, da der Rest bei der Division durch a natürlich betragsmäßig kleiner als a ist,
(2) erhält man aus 2sx −ex ≥ |x| 2−ex ≥ |a| ≥ |x| 2−ex − 1 ≥ 2sx −1−ex − 1 = 2sx −1−p−2sx +5 − 1 = 24−p−sx − 1 ≥
23−p−sx , da ja p ≤ −sx , d.h. 23−p−sx > 1.
Insbesondere ist zu beachten, daß −e z − ex > 0 gilt, so daß 2−ez −ex eine ganze Zahl ist:
ez + ex = 2ex − sx + p − 1 = 2p + 4sx − 10 − sx + p − 1
= 3(p + sx ) − 11 < 0
Für die Implementierung der Funktionen SIGN, SIZE, NORM und DENORM müssen wir noch einmal auf die Einzel(m)
mit m := pr12 (w) und e := pr22 (w).
heiten der Definition der Notation (·) D zurückgreifen: (w)D := 2(e)
21−lg(m)
Der Wert von SIGN((w)D ) läßt sich an m ablesen: Es gilt (w)D = 0 ⇔ (m) = 0 ⇔ m ∈ {0, 1}. Ansonsten ist
(w)D > 0 ⇔ erstes Bit von m = 0, die Bestimmung von SIGN ((w) D ) erfordert also nur einen konstanten Aufwand
für das Lesen der 2 ersten Bits von m.
(m)
Da 21 ≤ 21−lg(m)
< 1, gilt ganz einfach SIZE((w)D ) = (e) . Zur Bestimmung von SIZE((w)D ) sind also O(lg e)
Schritte ausreichend.
Um NORM(m, n) = m · 2n für zwei ganze Zahlen m, n zu berechnen, müssen wir nur den Exponenten richtig
behandeln:
(v)
NORM((v) , (u) ) = (v) · 2(u)
= 1−lg(v) · 2(u)
+1−lg(v) = (<v, ū>)D
2
wobei sich ū die Bedingung (ū) = (u) + 1 − lg(v) erfüllen soll. Da sowohl die binäre Addition als auch die
Bestimmung von lg(v) als Binärzahl linear in der Länge der Argumente sind, sind O(max{lg(v), lg(u)}) Schritte
zur Bestimmung von <v, ū> ausreichend.
Bei DENORM (x, n) = trunc(x · 2−n ) gehen wir wie folgt vor: Ist x = (w)D , pr12 (w) = m, pr22 (w) = e und
(u) = n, so gilt ja:
(m)
(e)
−(n)
·2
DENORM(x, n) = trunc
2lg(m)−1
46
4 FLIESSKOMMA-ARITHMETIK
Setzen wir i := (e) − (n) , so ergibt sich DENORM(x, n) = (v̄) mit
λ , falls i ≤ 0
v̄ :=
erste i + 1 Zeichen von m000 . . . , falls i > 0
Um zu testen, ob i = (e) − (u) ≤ 0 ist, sind O(lg(u)) Schritte ausreichend. (Wegen der Normierung in der
Definition von (·) muß e für diesen Test nicht unbedingt ganz gelesen werden!) Nur wenn i > 0 ist, bestimmen wir
den Wert von i durch Ausführung der Subtraktion. Als Schranke für den Aufwand zur Bestimmung von v̄ erhalten
wir damit:
O(lg(u)),
falls (e) − (u) ≤ 0
O(max{lg(e), lg(u)}) + O((e) − (u) ), falls (e) − (u) > 0
2
Obwohl wir bei den Operationen auf den dyadischen Zahlen den ganzzahligen Operanden P für die Festlegung der
gewünschten Genauigkeit benutzt haben, werden wir uns bei der Untersuchung der Komplexität auf Werte n ∈
beschränken, wobei uns vor allem das asymptotische Verhalten der Komplexität interessiert: Im wesentlichen werden
wir Schranken für die Zeit suchen, die wir brauchen um einen Funktionswert mit einem Fehler von höchstens 2 −n
zu bestimmen.
Allerdings gibt es keine Schranke, die für alle dyadischen Argumente gültig ist: Ist z.B. der Funktionswert größer als
2i , so brauchen wir bereits i + n Schritte, um eine ausreichend lange Mantisse zu erzeugen. Dies läßt sich durch eine
Schranke mit alleinigem Parameter n nicht beschränken. Daher formulieren wir im folgenden Komplexität abhängig
von der gewünschten Genauigkeit und zusätzlich von dem Bereich, aus dem die Argumente sind.
4.4 Lemma. Für die Operationen ADD, SUB, MULT und INV auf den dyadischen Zahlen gibt es Turingmaschinen
MADD , MSUB , MMULT und MINV mit folgenden Eigenschaften (dabei ist c ∈ eine Konstante):
Für alle m, n ∈ , x, y ∈ D und für alle w mit ((·) D , (·)D , (·) )(w) = (x, y, −n) und |(x)D | , |(y)D | ≤ 2m gilt:
TMADD (w) ≤ c · (m + n + 1)
TMSUB (w) ≤ c · (m + n + 1)
TMMULT (w) ≤ c · M(m + n + 1)
für alle m, n ∈ , x, ∈ D \ {0} und für alle w mit ((·) D , (·) )(w) = (x, −n) und |(x)D | ≥ 2−m gilt:
TMINV (w) ≤ c · M(m + n + 1)
2
Anmerkung: Zur Abschätzung der Komplexität reichen also die Kenntnis des ‘Genauigkeitsparameters’ n und einer
oberen Schranke für den Betrag der betreffenden dyadischen Zahlen (bzw. einer unteren Schranke bei INV), die
Länge der ‘Mantisse’ muß nicht bekannt sein! Die Komplexitätsuntersuchungen können also auf der (‘ideellen’)
Ebene der dyadischen Zahlen durchgeführt werden.
Beweis: Im folgenden verwenden wir wieder die Bezeichnungen für die Werte der Variablen, wie sie im vorigen
Lemma benutzt wurden. Allerdings haben wir jetzt p = −n und können bei den Abschätzungen s x , sy ≤ m verwenden (bzw. sx ≥ −m bei INV).
(a) Bei der Bestimmung der Komplexität von Addition und Subtraktion sieht man zunächst, daß die zwei notwendigen Denormalisierungen höchstens O(m + n) Schritte erfordern. Ferner sind die ganzzahligen Werte a und b
beide sicher durch 2m+n beschränkt, so daß ihre Addition (bzw. Subtraktion) ebenfalls in O(m + n) Schritten abgeschlossen werden kann. Das Ergebnis c wiederum ist sicher durch 2 m+n+1 beschränkt, so daß die abschließende
47
4 FLIESSKOMMA-ARITHMETIK
Normalisierung von c auch nur O(m + n) Schritte erfordert.
(b) Bei der Multiplikation muß (nach dem in konstanter Zeit möglichen Vergleich der Argumente mit 0) festgestellt
werden, ob −n größer ist als sx + sy . Dazu dürfen wir jedoch auf keinen Fall zunächst s x und sy einfach addieren:
Beide Werte können einen beliebig großen Betrag haben, wenn x und y fast Null sind. Daher gibt es keine (auch
noch so große) Schranke für die Komplexität der Bestimmung von s x + sy , die für alle x, y mit |x| , |y| ≤ 2m gilt.
Wir implementieren den Test IF P > SIZE(X)+SIZE(Y) THEN... daher wie folgt:
VAR TEST:BOOLEAN;
...
IF P <= 2*SIXE(X) THEN
TEST := ( P-SIZE(X) > SIZE(Y) )
ELSIF P <= 2*SIXE(Y)
TEST := ( P-SIZE(Y) > SIZE(X) )
ELSE
TEST := TRUE;
END
IF TEST THEN ...
Die Äquivalenz der zwei Programmstücke ist naheliegend: Ist P <= 2*SIXE(X) oder P <= 2*SIXE(Y), so ergibt sich der Wert von TEST aus einer Umformulierung des ursprünglichen Ausdrucks P > SIZE(X)+SIZE(Y).
Ansonsten ist P >2*SIXE(X) und P >2*SIXE(Y) und damit 2*P >2*SIXE(X)+ 2*SIXE(Y), in diesem
Fall sind also sowohl der Ausdruck P >SIXE(X)+ SIXE(Y) als auch die Variable TEST wahr.
Für die Abschätzung der Komplexität ist das zweite Programmstück jedoch geeigneter: Für die Vergleich P <= 2*SIXE(X)
reichen O(log n) Schritte. Fällt der Vergleich positiv aus, bestimmen wir P - SIZE(X). Hier ist jedoch einerseits
−n ≤ 2 · sx und andererseits sx ≤ m, also sicherlich |sx | ≤ m+n. Die Bestimmung von P - SIZE(X) und der
Vergleich mit SIZE(Y) sind also in höchstens O(log(n+m)) Schritten möglich. Das gesamte Programmstück kann
also im O(log(n+m)) Schritten abgearbeitet werden.
Ergibt sich schließlich bei TEST der Wert FALSE, so gilt −n ≤ s x + sy zusätzlich zu sx , sy ≤ m. Es gilt also
−n − m ≤ sx , sy ≤ m und damit |sx | , |sy | ≤ m + n. Dies ergibt einerseits die (grobe) Abschätzung |e x | , |ey | , |e| ≤
c1 · (m + n + 1) für eine geeignete Konstante c 1 und andererseits wiederum die Schranke O(log(n+m)) für die
Komplexität ihrer Berechnung.
Der Aufwand für die Denormalisierungen ist also durch O(n+m) beschränkt; die Werte |a|, |b| und |c| sind entsprechend durch 2c2 (n+m+1) für geeignetes c2 abschätzbar. Die Multiplikation c = a · b erfordert maximal O(M(c 2 (n +
m + 1))) Schritte, was wegen der Bedingung M(2i) ≤ 2M(i) durch O(M(n + m)) beschränkt ist. Die abschließende Normalisierung ist wiederum in O(n + m) Schritten möglich.
Insgesamt ist die Komplexität also durch O(M(n + m)) beschränkt.
(c) Bei der Berechnung des Kehrwertes ist der Test P>-SIZE(X) unkritisch, er kostet nicht mehr als O(log n)
Schritte. Fällt er negativ aus, so haben wir −n ≤ −s x zusätzlich zu der Voraussetzung sx ≥ −m, also n ≥
sx ≥ −m. Daraus erhalten wir |ex | , |ez | ≤ c3 (m+n). Den Wert 2**(-EZ-EX) können wir damit in O(m+n)
Schritten bestimmen: er besteht i.W. aus einer Folge von e z + ex Nullen (bei ν !). Alle weiteren Zeitabschätzungen
können analog zu oben durchgeführt werden, so daß sich schließlich wiederum O(M(n + m)) als Schranke für die
Komplexität ergibt.
2
4.5 Lemma.
Die Komplexität von a Die Komplexität von a Die Komplexität von a b und a p b ist O(max(log 2 a, log 2 b, |p|)).
p b ist O(M(max(log 2 a, log 2 b, |p|))).
p b ist O(M(max(log 2 a, − log 2 b, |p|))).
p
48
2
4 FLIESSKOMMA-ARITHMETIK
Beweis: In allen Fällen außer der Division sind die Abschätzungen naheliegend. Bei der Division erhalten wir für
die Komplexität von 1 p a unter Verwendung der Iterationsverfahrens im Bereich [1, 2) die Zeitschranke
dlog2 |p|e
X
i=0
M(2i + 3) ≤
dlog2 |p|e
X
i=0
2−i M(2dlog2 |p|e + 3) = O(M(|p|)
wenn wir die zusätzliche Eigenschaft 2M(n) ≤ M(2n) ≤ cM(n) aus der Festlegung der Komplexitätsschranke
M verwenden.
2
Das Verfahren zur Berechnung des Kehrwertes ist nur ein Spezialfall einer allgemeineren Vorgehensweise: Die verwendete Funktion Φ war quadratisch konvergent, d.h. es galt |Φ(x) − Φ(a)| ≤ L · |x − a| 2 in einer geeigneten
Umgebung von a.
Solche quadratisch konvergenten Verfahren erhält man automatisch beim Newton-Raphson-Iterationsverfahren zur
Nullstellenbestimmung, das wir später noch exakt untersuchen werden. Zunächst werden wird jedoch die Bedingungen an die Komplexitätsschranken genauer untersuchen. Sie haben den Vorteil, dass man mit ihnen besonders leicht
die Komplexität von Algorithmen abschätzen kann
4.6 Definition. Eine Zeitschranke t : → heißt regulär, wenn sie monoton wachsend ist, nicht konstant den Wert
0 hat und zudem c, n0 ∈ existieren, so dass für alle n ≥ n0 gilt
(1)
(2)
2t(n) ≤ t(2n) ≤ c · t(n)
2
4.7 Lemma. Für jede reguläre Zeitschranke t :
→
gilt
(a) t wächst mindestens linear, d.h. id ∈ O(t).
(b) Es gibt zu t ein k ∈ , so daß t ∈ O(nk ) gilt.
(c) Für alle c̄ ∈
(d) Es ist
dlog
2 ne
P
k=0
ist t(c̄ · n + c̄) ∈ O(t).
t(2k ) ∈ O(t).
2
Beweis: Sei t regulär und c, n0 seien die entsprechenden Konstanten. k 1 sei so groß gewählt, daß sowohl n1 :=
2k1 > n0 als auch c1 := t(n1 ) > 0 gilt.
(a) Für n ≥ n1 gilt mit Bedingung (1)
t(n) ≥ t 2blog2 nc
≥ t(2k1 ) · 2blog2 nc−k1
≥ c1 · n · 2−1−k1
Also gilt sicherlich n ≤ c̄ t(n) für eine geeignete Konstante c̄ ∈ .
(b) Für n ≥ n1 gilt mit Bedingung (2)
t(n) ≤ t 2dlog2 ne
≤ t 2k1 · cdlog2 ne−k1
≤ c1 · nlog2 c · c1−k1
49
4 FLIESSKOMMA-ARITHMETIK
d.h. t ∈ O(nlog2 c ).
(c) Sei c̄ ∈ . Für alle n ≥ n0 > 1 gilt dann mit Bedingung (2)
t(c̄n + c̄) ≤ t(2c̄n)
≤ t 2dlog2 c̄e+1 · n
≤ cdlog2 c̄e+1 · t(n)
(d) Setzen wir c2 :=
k1
P
k=0
t(2k ), so ergibt sich für alle n ≥ n1
dlog2 ne
X
k=0
t(2k ) =
k1
X
t(2k ) +
X
t(2k )
k=k1 +1
k=0
≤ c2 +
dlog2 ne
∞
X
k=0
2−k · t 2dlog2 ne
≤ c2 + t(2n) ·
∞
X
k=0
2−k = c2 + t(2n) · 2
≤ c2 + c3 · t(n)
2
4.8 Lemma.
(i) Die Identität auf
ist regulär.
0
0
0
(ii) Ist t regulär und t0 :
→ +
0 monoton wachsend, nicht identisch 0 mit t (2n) ≤ c · t (n) für gewisse
0
0
0
0
c , n0 ∈ und für alle n ≥ n0 , so ist auch die Funktion bt(n) · t (n)c regulär.
2
Beweis: (i) ist offensichtlich: 2 id(n) = id(2n)
Bei (ii) ergibt sich mit der Regularitätsbedingung (1) für t und der Monotonie von t 0
2bt(n) · t0 (n)c ≤ 2bt(n) · t0 (2n)c ≤ t(2n) · t0 (2n)
Mit der Regularitätsbedingung (2) für t und der analogen Bedingung für t 0 erhalten wir für alle n ≥ n1 :=
max{n0 , n00 } (wobei wir o.B.d.A. t(n1 ) · t0 (n1 ) > 1 annehmen):
bt(2n) · t0 (2n)c ≤ bc · t(n) · t0 (2n)c
≤ bc · t(n) · c0 t0 (n)c ≤ c · c0 · t(n) · t0 (n)
≤ c · c0 bt(n) · t0 (n)c + 1 ≤ 2cc0 bt(n) · t0 (n)c
2
Aus dem Lemma ergibt sich, daß zum Beispiel alle Funktionen t der Form t(n) = bn α (log n)β (log log n)γ c, für
α ≥ 1, β, γ ≥ 0, α, β, γ ∈ , regulär sind.
Anmerkung: Die Bedingungen aus der Definition der regulären Zeitschranken implizieren außer einem (a) Mindestund einem (b) Höchstwachstum auch eine (c) Gleichmäßigkeit im Wachstum. Es gibt Funktionen, die zwar (a) und
50
4 FLIESSKOMMA-ARITHMETIK
(b) aus dem vorigen Lemma genügen, die aber dennoch nicht regulär sind: Betrachte t(n) := n · max{i! | i! ≤ n}.
Damit ist sicherlich n ≤ t(n) ≤ n2 und auch 2t(n) ≤ t(2n),
aber die Bedingung (2) aus der Definition ist nicht
m!
1
m!
2
=
erfüllt: Für jedes m > 2 ist t(m!) = (m!) 2 und t m!
2
2 · (m − 1)! = 2m (m!) . Für n = 2 ist also
2
t(2n) = (m!) = 2mt(n).
Da bei einer Berechnung auf dyadischen Zahlen zum Erreichen einer Genauigkeit von 2 −n in der Regel auch ein Wert
bestehend aus O(n) Bits erzeugt werden muß, ist das zumindest lineare Wachstum einer regulären Zeitschranke keine
spürbare Einschränkung. Andererseits ist jede reguläre Schranke durch ein Polynom beschränkt, wir können also
nur den Bereich der Polynomzeit-berechenbaren Funktionen mit regulären Schranken erfassen! Nach dem obigen
Lemma können wir in diesem Bereich jedoch sehr genaue Aussagen über die (asymptotische) Komplexität von
Funktionen machen.
Als erstes Beispiel, bei dem wir die Regularität der Komplexitätsschranken intensiv nutzen, betrachten wir die Ableitung einer berechenbaren Funktion.
4.9 Lemma. Die Funktion f : sei auf G = [a, b] (mit a, b ∈ D) stetig differenzierbar mit einer Ableitung
f 0 , die auf G sogar Lipschitz-stetig ist. Ist f auf G in Zeit O(t) berechenbar für eine reguläre Schranke t, so ist f 0
ebenfalls auf G in Zeit O(t) berechenbar.
2
Beweis: Da f 0 Lipschitz-stetig ist, gibt es ` ∈ mit |f 0 (x) − f 0 (y)| ≤ 2` |x − y| für alle x, y ∈ [a, b]. Ferner ist f
¯
selbst als differenzierbare Funktion auf [a, b] auch Lipschitz-stetig mit |f (x) − f (y)| ≤ 2 ` |x − y| für ein `¯ ∈ .
Um nun f 0 (x) für ein x ∈ [a, b] zu approximieren, verwenden wir den Mittelwertsatz, nachdem für jedes y ∈
[a, b], y 6= x, ein z zwischen x und y existiert, so daß f 0 (z) = (f (y) − f (x))/(y − x) gilt. Ist |y − x| ausreichend
klein, so ist auch |f 0 (z) − f 0 (x)| ≤ 2` |z − x| ≤ 2` |y − x| klein. Wir können f 0 (x) also durch die Auswertung von
(f (y) − f (x))/(y − x) approximieren. Allerdings werden dann die Fehler, die wir bei der Bestimmung von f (x)
und f (y) machen, durch die Division durch (y − x) enorm vergrößert. Wir müssen also f (x) und f (y) sehr genau
auswerten, um bei f 0 (z) eine passable Näherung an f 0 (x) zu gewinnen. (Da in der Praxis meist mit fest vorgegebenen
Genauigkeiten gerechnet wird, wird die Differentiation auch als numerisch nicht stabil bezeichnet!)
Bei gegebenem x ∈ [a, b] müssen wir ein y verwenden, das nahe bei x liegt und zudem noch im Intervall [a, b] ist,
damit wir f (y) überhaupt bestimmen können. Daher seien q ∈ D und d ∈ so gegeben, daß a ≤ q − 2 −d und
q + 2−d ≤ b gilt.
Die Einzelheiten der Bestimmung von f 0 finden sich im folgenden Algorithmus; der entsprechend Lemma 6.6 eine
Funktion F : D ×
D zur Berechnung von f benutzt.
FUNCTION F_ABL (X:DYADIC; P: WHOLE)
VAR P1, P2, P3, P4: WHOLE;
Y, FX, FY, FZ: DYADIC;
BEGIN
P1:=MIN(P-`-2,-d-2);
P2:=P-2;
¯
P3:=MIN(P+P1-`-3,P1);
P4:=P+P1-3;
FX:=F(X,P4);
IF SUB (X,q,-d-1) ≥ 0 THEN
Y:= SUB(X,2P1,P3);
FY:=F(Y,P4);
FZ:=SUB(FX,FY,P4);
ELSE
Y:= ADD(X,2P1,P3);
FY:=F(Y,P4);
FZ:=SUB(FY,FX,P4);
END
RETURN SHIFT(FZ,-P1,P2);
51
4 FLIESSKOMMA-ARITHMETIK
END;
Dabei sei die Funktion SHIFT:D × × → D durch |SHIFT(x, q, p)−2 q ·x| ≤ 2p festgelegt, d.h. sie multipliziert x
mit der Zweierpotenz 2q . Wie man sich leicht überlegt, kann SHIFT durch NORM(DENORM (x, p − q), p) implementiert werden, so daß sich bei i, j, k ∈ und |x| ≤ 2 i die Komplexität von SHIFT (x, j, −k) durch c(i + j + k + 1)
abschätzen läßt .
Es seien wie üblich x, y, fx , . . . die Werte der Variablen X, Y, FX,. . .. Ferner sei z der Wert, der von der Funktion
berechnet und zurückgegeben wird. Insbesondere sei x so gegeben, daß a ≤ x ≤ b gilt.
Im folgenden sei zunächst SUB(x, q, −d − 1) ≥ 0. Dann ist sicher x − q + 2 −d−1 ≥ 0, d.h. x ≥ q − 2−d−1 . Wir
erhalten dann auch x − 2p1 ≥ a, y ≥ x − 2p1 − 2p3 ≥ a sowie y ≤ x − 2p1 + 2−p3 ≤ x ≤ b. Damit gilt sowohl
x − 2p1 ∈ [a, b] als auch y ∈ [a, b].
Ferner erhalten wir mit dem Mittelwertsatz für ein ξ ∈ [x − 2 p1 , x] die folgende Abschätzung
|f 0 (x) − z| ≤
≤
≤
≤
=
≤
≤
=
2p2 + |f 0 (x) − fz · 2−p1 |
2p2 + 2p4 −p1 + |f 0 (x) − (fx − fy ) · 2−p1 |
2p2 + 2p4 −p1 + 2 · 2p4 −p1 + |f 0 (x) − (f (x) − f (y)) · 2−p1 |
¯
2p2 + 3 · 2p4 −p1 + 2`+p3 −p1
+ |f 0 (x) − (f (x) − f (x − 2p1 )) · 2−p1 |
¯
2p2 + 3 · 2p4 −p1 + 2`+p3 −p1 + |f 0 (x) − f 0 (ξ)|
¯
2p2 + 3 · 2p4 −p1 + 2`+p3 −p1 + 2`+p1
2p−2 + 3 · 2p−3 + 2p−3 + 2p−2
2p
Die gleiche Abschätzung erhalten wir im Fall SUB(x, q, −d−1) < 0. Der Algorithmus berechnet also wie gewünscht
die Ableitung f 0 .
Zur Abschätzung der Komplexität des Algorithmus sei n ∈ gegeben, es sei p := −n. O.B.d.A sei n so groß, daß
n > `, −n − ` − 2 < −d − 2, also p1 = −n − ` − 2, und −n − `¯ − 3 < 0, also p3 = −2n − ` − `¯ − 5, gilt. Ferner
sei m ∈ so gewählt, daß 2m größer ist als |a|, |b|, max{f (t) | t ∈ [a, b]} und max{f 0 (t) | t ∈ [a, b]}. Dann ist die
Komplexität beschränkt durch
+
+
+
+
+
c1 (n + 1)
2 · t(2n + ` + 5)
c2
c3 (m + n + 1)
c4 (m + n + 1)
c5 (m + n + 1)
(Bestimmung von p1 bis p4 , grobe Abschätzung)
(Bestimmung von fx und fy )
(Vergleich x und q )
(Bestimmung von y)
(Bestimmung von fz )
(SHIFT)
Da t nach Voraussetzung regulär ist, liegt die Komplexität also in O(t).
2
Viele interessante Zahlen und Funktionen sind über Reihen definiert, z.B. e, π, sin(x), e x . . ., wobei einerseits die
Reihenglieder durch elementare Rechnungen beliebig genau approximiert werden können und andererseits der Restfehler bei einer nur endlichen Zahl von Summanden leicht abgeschätzt werden kann (z.B. mit Hilfe des letzten Summanden bei alternierenden Reihen). Im folgenden betrachten wir das Konzept eines Algorithmus zur Auswertung
einer konvergenten Reihe.
P∞
ai eine konvergente Reihe.
an, daß uns Funktionen A : × → D und
P Wir nehmen
∞
R : → mit |A(i, p) − ai | ≤ 2−p und 2R(i) ≥ j=i+1 aj zur Verfügung stehen. Mit diesen Mitteln können wir
P
nun den Wert der Reihe ∞
i=1 ai beliebig genau approximieren. Interessant dabei ist, daß wir die Zwischenberechnungen mit unterschiedlicher Genauigkeit durchführen, da wir im Allgemeinen nicht wissen, wieviele Glieder der
Reihe wir auszuwerten haben.
4.10 Beispiel. Es sei
i=1
52
4 FLIESSKOMMA-ARITHMETIK
FUNCTION . . . (. . . , P:WHOLE):DYADIC;
VAR Z,Y: DYADIC;
(*Summe und Summand*)
S: NATURAL;
(*variable Zahl von ’Schutzziffern’*)
I: NATURAL;
(*Summationsindex*)
L: NATURAL;
(*Hilfsvariable für S*)
BEGIN
Z:=0; I:=0; S:=1; L:=1;
WHILE R(I)>P-1 D0
I:=I+1;
IF I≥L THEN S:=S+2; L:=2*L END;
Y:=A(I,P-S);
Z:=ADD(Z,Y,P-S);
END;
RETURN Z;
END;
p sei der Wert des Parameters P beim Aufruf der Funktion. z i , yi , li und si seien die Inhalte der entsprechenden
Variablen Z, Y , L und S zum Zeitpunkt des Testes zu Beginn der i + 1-ten Durchlaufes der WHILE-Schleife (also
wenn I den Wert i hat). Dann gilt sofort
| yi − ai |≤ 2p−si
und mit Induktion li = 2(si −1)/2 (im Bereich 2k−1 ≤ i < 2k ist li = 2k und damit gilt si = 2k+1) sowie
i
i
X
X
zi −
2p−sj +1
aj ≤
j=1
j=1
denn für i = 0 gilt
0
P
z
−
a
a
=
0
=
z
,
also
= 0; für i > 0 folgt
0
j
j
0
j=1
j=1 P0
i−1
i
X
X
+ |ai − zi + zi−1 |
zi −
a
z
−
≤
a
j
i−1
j
j=1
j=1
≤
i−1
X
2p−sj +1 + |ai − yi | + |yi + zi−1 − zi |
≤
i−1
X
2p−sj +1 + 2p−si + 2p−si
=
i
X
2p−sj +1
j=1
j=1
j=1
53
4 FLIESSKOMMA-ARITHMETIK
d sei die Zahl der Durchläufe durch die WHILE-Schleife. Daraus ergibt sich nach Wahl der Funktion R für den von
der Funktion zurückgegebenen Wert z d :
∞
∞
d
X
X
X
zd −
a
=
a
+
a
z
−
j
j
j
d
j=1
j=1
j=d+1
≤ 2
p−1
+
d
X
2p−sj +1
j=1
= 2p−1 + 2p+1
d
X
2−sj
= 2p−1 + 2p+1
j=1
∞
X
= 2p−1 + 2p+1
∞
X
2−(2k+1) · card{j | 1 ≤ j ≤ d, 2k−1 ≤ j < 2k }
≤ 2p−1 + 2p+1
∞
X
2−(2k+1) · 2k−1
k=1
k=1
= 2
p−1
+2
p+1
k=1
2−(2k+1) · card{j | 1 ≤ j ≤ d, sj = 2k+1}
· 1/4 = 2p
Der Algorithmus liefert also wie gewünscht eine Approximation mit einem Fehler von höchstens 2 p an den Wert der
Reihe.
Wie man sieht, kann die Anzahl sj der ‘Schutzbits’ beliebig gewählt werden, solange sichergestellt ist, daß die Summe
Pd
−sj durch 1/4 beschränkt ist. Wäre die Zahl d der zu summierenden Reihenglieder (also der Schleifendurchj=1 2
läufe) im voraus bekannt, so läge die Wahl von 2 −sj ≈ 1/4d, d.h. sj ≈ 2 + log2 d, nahe. Beim obigen Algorithmus
entwickeln sich die Schutzbits wie folgt:
i 1 2 3 4 5 6 7 8 9 10 . . .
si 3 5 5 7 7 7 7 9 9 9 . . .
Dabei gelten die Beziehungen li = 2(si −1)/2 und li ≤ 2i für alle i ≤ d. Also ist hier stets s j ≤ 3 + 2 log 2 d. Bei
regulären Schrankenfunktionen und der Untersuchung asymptotischer Komplexität ist diese Verdopplung der Zahl
der Schutzbits ohne Belang.
Bei konkreten Reihen sind für die Komplexität der Reihenauswertung meist zwei Parameter ausschlaggebend: Die
Zahl d(n) der Schleifendurchläufe bis zum Erreichen der Genauigkeit 2 −n und die Komplexität der Bestimmung
eines Reihengliedes mit Genauigkeit 2 −n . Die Komplexität der anderen Operationen, etwa der Additionen oder
die Auswertung von R, kann üblicherweise vernachlässigt werden. Falls z.B. die Komplexität zur BestimmungP
von
0 für die Komplexität von
A(i, −n) durch t(i+n)
(für
ein
reguläres
t)
beschränkt
ist,
erhalten
wir
als
Schranke
t
ai
P
d(n)
0
in etwa: t (n) = O
2
i=1 t(i + n+ log i) = O (d(n) · t(n+d(n))).
Mit dem obigen ‘Meta-Algorithmus’ können wir nun leicht die Komplexität vieler interessanter Funktionen abschätzen:
4.11 Lemma. Die Funktionen sin(x), cos(x) und e x sind auf jeder kompakten Menge G ⊆ in Zeit n/ log n·M(n)
berechenbar.
2
P∞
i
2i−1 /(2i − 1)!. Wir beschränken uns im folgenden auf die
Beweis: Bekannterweise ist sin(x) =
i=1 (−1) · x
Menge G := [−1, 1], x ∈ G sei beliebig, aber fest gewählt. Setze a i := (−1)i · x2i−1 /(2i − 1)! für i ≥ 1.
54
4 FLIESSKOMMA-ARITHMETIK
Da die Sinus-Reihe alternierend ist, können wir den Restfehler bei der Summation durch den letzten bekannten
Summanden abschätzen. Die Beschränkung |x| ≤ 1 erlaubt uns zudem eine sehr einfache Auswertung dieser Sum−x2
. Da der ergänzende Faktor kleiner als 1 ist, wird der Fehler, den wir
manden: Für i > 1 ist ai = ai−1 · (2i−2)·(2i−1)
bei der Auswertung der ai machen, von Summand zu Summand kleiner!
Im folgenden bauen wir die Sinus-Reihe in den Rahmen-Algorithmus aus dem vorigen Beispiel ein.
FUNCTION SINUS (X: DYADIC, P: WHOLE): DYADIC;
VAR
Y, Z, R, Q: DYADIC;
S, I, L: WHOLE;
BEGIN
IF P≥0 THEN RETURN 0;
Z:=0; I:=0; S:=1; L:=1;
Q:=MULT(X,X,P-6);
REPEAT
I:=I+1
IF I≥L THEN S:=S+2; L:=2*L END;
IF I=1 THEN
Y:=COPY(X,P-S-6);
ELSE
R:=MULT(Y,Q,P-S);
Y:=DIV(R,-(2*I-2)*(2*I-1),P-S-1);
END;
Z:= ADD(Z,Y,P-S);
UNTIL SIGN(Y)=0 OR SIZE(Y) ≤ P-2
RETURN Z;
END;
Zur Untersuchung der Korrektheit des Algorithmus betrachte x ∈ D ∩ [−1, 1] und p ∈ als Parameter beim
Aufruf von SINUS. Bei p ≥ 0 liefert der Algorithmus sofort den Wert 0 zurück, der in diesem Fall offensichtlich
ausreichend genau ist. Im folgenden sei daher p < 0. q sei der Wert der Variablen Q, z i , ri , . . . seien die Werte der
anderen Variablen jeweils am Ende der i-ten Durchlaufes durch die REPEAT-Schleife.
Zum Nachweis der Korrektheit des Algorithmus müssen wir nur zeigen, daß |y i − ai | ≤ 2p−si gilt: Dann haben die
einzelnen yi die im Rahmen-Algorithmus geforderte Genauigkeit, ferner ist die Abbruchbedingung des Rahmenalgorithmus erfüllt:
∞
X
p−si
a
≤ |yi | + 2p−3 ≤ 2p−1
k ≤ |ai | ≤ |yi | + 2
k=i+1
Zunächst stellen wir fest, daß sicherlich |q| ≤ 2 und stets s i ≤ si−1 + 2 gilt. Daraus ergibt sich für alle i ≥ 2 leicht
ai ≤ 23−si .
Die Bedingung |y1 − a1 | ≤ 2p−s1 (mit s1 = 3) wird durch die Funktion COPY sofort erfüllt. Für i > 1 gilt
ri
|yi − ai | ≤ 2p−si −1 + ai − (2i−2)(2i−1)
1
ai−1 x2 − ri ≤ 2p−si −1 + (2i−2)(2i−1)
1
≤ 2p−si −1 + (2i−2)(2i−1)
(2p−si + ai−1 x2 − yi−1 q )
1
≤ 2p−si −1 + (2i−2)(2i−1)
(2p−si + x2 − q · |ai−1 | + |q| · |ai−1 − yi−1 |)
≤ 2p−si −1 +
1
p−si
(2i−2)(2i−1) (2
+ 2p−6 · |ai−1 | + 2 · |ai−1 − yi−1 |)
Für i = 2 erhalten wir mit s2 = 5 und |y1 − a1 | ≤ 2p−6
1
|y2 − a2 | ≤ 2p−6 + (2p−6 + 1 · 2p−6 + 2 · 2p−6 ) ≤ 2p−5 = 2p−s2
6
55
4 FLIESSKOMMA-ARITHMETIK
und für i > 2 ergibt sich sofort mit der Induktionsvoraussetzung |y i−1 − ai−1 | ≤ 2p−si−1 :
1 p−si
(2
+ 2p−6 · 23−si−1 + 2 · 2p−si−1 )
20
1
= 2p−si −1 + (2 · 2p−si −1 + 2p−si−1 −1 + 16 · 2p−si −1 ) ≤ 2p−si
20
|yi − ai | ≤ 2p−si −1 +
Die Korrektheit des Algorithmus wäre damit sichergestellt. Zur Abschätzung seiner Komplexität sei p = −n für
n ∈ . Hauptbestandteil der Untersuchung ist dann die Bestimmung einer (guten) oberen Schranke d(n) für die
Zahl der Schleifendurchläufe, also der benötigten Summanden a i bzw. yi .
Das Programm hält, sobald sich |yi | ≤ 2−n−2 ergibt; da stets |yi | ≤ |ai | + 2−n−si ≤ |ai | + 2−n−3 gilt, ist dies
spätestens erfüllt, wenn |ai | ≤ 2−n−3 ist. Wegen |x| ≤ 1 ist jedoch |ai | ≤ 1/(2i−1)!, d.h. wir können d(n) :=
min{i | (2j−1)! ≤ 2n−3 } wählen,
Man kann leicht zeigen, daß d ∈ Θ(n/ log n) (d.h. d ∈ O(n/ log n) und n/ log n ∈ O(d)) gilt:
log 2 (n/ log 2 n − 1)! ≤ log 2 ((n/ log 2 n)n/ log2 n ) = n − n/log2 n · log2 log2 n
log 2 (3n/ log n − 1)! ≥ log 2 ((n/ log 2 n)2n/|log2 n ) = 2n − 2n/ log 2 n · log2 log2 n
Für ausreichend großes n ist damit n/ log 2 n ≤ d(n) ≤ 3n/ log 2 n.
Als Komplexitätsschranke für den Sinus-Algorithmus erhalten wir damit sofort t(n) = n/ log n · M(n). Da die
Taylor-Reihen für cos(x) und ex ähnlich aufgebaut sind (jeweils nächster Summand durch endlich viele Multiplikationen aus dem vorigen Summanden bestimmbar, Größenbeschränkung der Summanden über Fakultät), ergibt sich
dort die gleiche Komplexitätsschranke (für Argumente x ∈ G = [−1, 1]).
Analoge Algorithmen für umfangreichere Mengen G lassen sich durch höhere Genauigkeiten bei der Bestimmung
der Summanden erreichen. Man kann insbesondere auch die zu verwendende Genauigkeit an die Größe des Argumentes x binden. Ferner wird sehr oft Range Reduction eingesetzt, um Argumente x auf Bereiche zu transformieren,
bei denen eine leichtere Auswertung möglich ist, z.B. durch Ausnutzung der Periodiziät der Winkelfunktionen oder
durch Verwendung von Formeln wie
sin(x) = 3 · sin(x/3) − 4 · sin(x/3)3
oder (Scaling and Squaring)
ex = ( ex/2 )2
bzw.
lnx = ln(x/2i ) + i · ln2
und
√
lnx = 2 · ln x
2
Schnellere Algorithmen (Komplexität log n · M(n)) sind 1976 von R. P. Brent entwickelt worden, er weist die
Gültigkeit dieser Schranke z. Bsp. für arctan(x), log(x), e x , sin(x), und cos(x) nach. Die notwendigen Hilfsmittel
werden wir zum Teil noch behandeln.
4.12 Beispiel. Ähnlich wie bei der Behandlung von Reihen können wir auch bei Iterationsverfahren einen Rahmen
für einen Algorithmus angeben: Es sei f : eine Funktion mit einem Fixpunkt z, d.h. f (z) = z. Ferner seien
z1 ∈ D, α, ` ∈ und ε ∈ so gegeben, daß |z − z1 | ≤ 2ε und (∀x ∈ )|x − z| ≤ 2ε ⇒ |f (x) − z| ≤ 2` · |x − z|α
gilt. Die Funktion F : D × D approximiere f in üblicher Weise, d.h. |F (x, p) − f (x)| ≤ 2 p .
56
4 FLIESSKOMMA-ARITHMETIK
FUNCTION . . . (. . .,P:WHOLE):DYADIC;
VAR Z: DYADIC;
E: WHOLE;
BEGIN
Z:= z1 ; E:= ε;
WHILE E > P DO
E:= dα · E + `e + 1;
Z:= F (Z,E-1);
END;
RETURN Z;
END;
Es seien p der Wert der Variablen P und ε i , zi die Werte der Variablen E bzw. Z beim i-ten Test der WHILE-Bedingung.
Wir nehmen im folgenden an, daß stets ε i < εi−1 sei, also auch εi ≤ ε−(i−1). Dann gilt mit Induktion |z i −z| ≤ 2εi :
Für i = 1 ist dies nach Voraussetzung erfüllt, bei i > 1 erhalten wir
|zi − z| =
≤
≤
=
|F (zi−1 , εi − 1) − z|
|F (zi−1 , εi − 1) − f (zi−1 )| + |f (zi−1 ) − z|
2εi −1 + 2` · (2εi−1 )α
2εi −1 + 2`+αεi−1
Wegen εi = d` + αεi−1 e + 1 gilt jedoch ` + αεi−1 ≤ d` + αεi−1 e ≤ εi − 1 und damit |zi − z| ≤ 2εi .
Nach maximal i = ε − p Schleifendurchläufen muß gelten ε i ≤ p, also endet der Algorithmus und liefert, wie
verlangt, eine Approximation an z mit einem Fehler kleiner oder gleich 2 p .
Für die Korrektheit des Algorithmus muß demnach nur noch sicher gestellt werden, daß die Folge (ε i )i≥1 streng
monoton fällt. Dies ist natürlich vom verwendeten Iterationsverfahren und dem Startwert abhängig, d.h von den
Parametern α, ε und `.
Ist α = 1, so liegt ein lineares Iterationsverfahren vor und es ist
εi = dεi−1 + `e + 1 = εi−1 + d`e + 1 = ε + (i − 1)(d`e + 1)
p−ε
Wir benötigen also ` ≤ −2. Als Gesamtzahl der Durchläufe durch die Schleife erhalten wir dann max{ d`e+1
, 0}.
Benötigen wir zur Auswertung von F (x, −n) für x ∈ [z − ε, z + ε] jeweils t(n) Schritte (für ein reguläres t), so ist
die Komplexität t0 (n) zur Approximation von z mit Genauigkeit 2 −n beschränkt durch t0 (n) = O(n · t(n)). Diese
Schranke gilt auch bei einem beliebigen ` mit 0 > ` > −2. Allerdings müssen wir dann den obigen Algorithmus
ˆ
modifizieren, z.B. indem wir statt f die Funktion f(x)
= f (f (. . . f (x) . . .) mit m = d−2/`e (und `0 = m · ` < −2)
| {z }
m
verwenden.
Ist α > 1, so liegt ein superlineares Iterationsverfahren vor und wir haben ε i = dαεi−1 + ` + 1e ≤ αεi−1 + ` + 2
und damit
εi ≤ αεi−1 + ` + 2
= α(αεi−2 + ` + 2) + ` + 2
i−2
X
αj
= . . . = αi−1 ε + (l + 2)
j=0
αi−1
−1
α−1
l+2
l+2
)−
= αi−1 (ε +
α−1
α−1
= αi−1 ε + (l + 2)
57
4 FLIESSKOMMA-ARITHMETIK
l+2
, was wir
Als hinreichende Bedingung für das streng monotone Fallen der Folge (ε i ) erhalten wir also ε < − α−1
durch geeignete Wahl des Startwertes stets erreichen können.
Um die Komplexität des Verfahrens zu bestimmen, sei wieder t(n) eine reguläre Schranke für den Aufwand zur
l+2
Bestimmung von F (x, −n), n ∈ . Wir setzen γ 1 := − α−1
und γ2 := ε + γ1 (< 0). O.B.d.A. sei n größer als γ1 .
2n
Dann gilt für k := dlog α −γ2 e
αk γ2 − γ 1 ≤
2n
· γ2 − γ1 = −2n − γ1 ≤ −n
−γ2
Also wird die WHILE-Schleife höchstens k-fach durchlaufen.
Ferner ist stets εi = dαεi−1 + l + 1e ≥ αεi−1 + l + 1 ≥ . . . ≥ αi−1 ε +
l+1
α−1
−
l+1
α−1
= −αi−1 γ3 − γ4 . Sei
m so groß, daß ab αm−1 die Regularitätsbedingungen für t greifen; dann ist die Komplexität t 0 bei superlinearen
Verfahren wie folgt beschränkt, wenn n ausreichend groß ist:
t0 (n) ≤
k
P
t(αi−1 γ3 + γ4 + 1)
i=1
≤ c1 + c1
≤ c2 + c2
≤ c2 + c2
k
P
t(αi−1 )
i=1
k
P
i=m
k
P
t(αi−1 )
t 2d(i−1) log2 αe
i=m
∞
P
t 2d(k−1) log2 αe · 2−j
j=0
log2 αe
= c2 + c2 · 2t 2d(k−1)
≤ c2 + c2 2t 2αk−1 2n
≤ c2 + c2 2t 2b −γ
c
2
≤ c2 + c2
≤ c3 + c3 t(n)
(Die Komplexität des Tests der Schleifenbedingung und der Bestimmung der ε i kann dabei vernachlässigt werden.)
Bei jedem superlinearen Verfahren bleibt also die Komplexität in der Klasse O(t) (wenn t regulär ist)!
Anmerkung: Bei einer praktischen Anwendung sollte man darauf achten, daß das „letzte“ ε i nicht wesentlich kleiner
als p ist, da ein großer Teil des Gesamtaufwandes in den letzten Iterationen geschieht. Ist z.B. ε i = −2i und p =
−2n − 1, so ergibt sich bei der vorletzten Iteration ε k−1 = −2n und bei der letzten Iteration εk = −2n+1 , d.h. eine
Genauigkeit, die deutlich zu hoch ist.
2
Wir hatten bereits die Bestimmung des Kehrwertes mit einem quadratisch konvergenten Iterationsverfahren durchgeführt. Analog kann man die Quadratwurzel mit der Iterationsfunktion f (x) = (x + a/x)/2 berechnen (herleitbar
über das Newton-Raphson-Verfahren). Daraus ergibt sich:
4.13 Korollar. Die Funktion
√
ist auf jedem beschränkten G ⊆ +
0
in Zeit O(M) berechenbar.
2
4.14 Lemma. Die Funktion f : sei auf dem Intervall [a, b] (für a, b ∈ D) differenzierbar, wobei die Ableitung
f 0 auf [a, b] Lipschitz-stetig sei und an keiner Stelle den Wert 0 habe. Dann besitzt f auf [a, b] eine Umkehrfunktion
f −1 . Ist f in Zeit O(t) auf [a, b] berechenbar, so ist f −1 ebenfalls in Zeit O(t) auf {f (x)|x ∈ [a, b]} berechenbar.
2
Beweis: Da f 0 auf [a, b] stetig ist, aber den Wert 0 nicht annimmt, ist f auf [a, b] streng monoton, also existiert auf
[a, b] die Umkehrfunktion f −1 , wobei f −1 (y) die einzige Nullstelle von f (x)−y ist. Um f −1 (y) aus y zu berechnen,
58
4 FLIESSKOMMA-ARITHMETIK
verwenden wir das Newton-Raphson’sche Iterationsverfahren: Wir setzen I := {f (x) | x ∈ [a, b]} und definieren
F : [a, b] × −→ durch F (x, y) := x − ff(x)−y
0 (x) . Da f auf [a, b] in Zeit O(t) berechenbar ist, ist auch F (x, y) auf
[a, b] × I in Zeit O(t) berechenbar.
¯
Es sei 2` eine Lipschitzschranke für f 0 , ferner sei s ∈ so gewählt, daß für x ∈ [a, b] stets 2 s ≤ |f 0 (x)| gilt.
Dann gilt für alle y ∈ I und x ∈ [a, b] mit z := f −1 (y):
(x−z)f 0 (x)+f (z)−f (x) |F (x, y) − z| = x − ff(x)−y
0 (x) − z = 0
f (x)
0
0 (ξ)(z−x) f 0 (x)−f 0 (ξ) = (x−z)f (x)+f
=
|x
−
z|
f 0 (x)
f 0 (x)
`¯
¯
`−s
≤ |x − z| · 2 f(x−z)
|x − z|2
0 (x) ≤ 2
für ein ξ ∈ [a, b] zwischen x und z (Mittelwertsatz!).
Für jedes y ∈ I ist F (x, y) also eine quadratisch konvergierende Iterationsfunktion mit Fixpunkt z = f −1 (y).
Um unser allgemeines Verfahren zur Fixpunktbestimmung anwenden zu können, definieren wir zunächst F̄ (x, y) :
× → durch

 F (a, y), x ≤ a
F (x, y), a ≤ x ≤ b
F̄ (x, y) :=

F (b, y), b ≤ x
Setzen wir ` := `¯ − s, so gilt für jedes y ∈ I und jedes x ∈ sogar auf × I in Zeit O(t) berechenbar ist.
| F̄ (x, y) − f −1 (y)| ≤ 2` |x − f −1 (y)|2 , wobei F̄
Für die Anwendung unseres Verfahrens zur Fixpunktbestimmung brauchen wir nur noch einen zu dem jeweiligen y
`+1
passenden Startwert z1 mit einer Ungenauigkeit von maximal 2 ε mit ε < − α−1
(mit α = 2), also etwa ε := −` − 2
(unabhängig von y!), zu wählen.
Diese Wahl können wir sogar in konstanter Zeit vornehmen:
Es sei 2s̄ eine Lipschitzschranke für f . Setze x 0 := b und xi := a + (i − 1)2ε−1+s−s̄ ; k sei maximal mit xk ∈ [a, b]
gewählt. Zu jedem xi (0 ≤ i ≤ k) bestimme yi mit |yi − f (xi )| ≤ 2ε−2+s .
Damit gibt es sicher zu jedem y ∈ I ein i ≤ k mit |x i − f −1 (y)| ≤ 2ε−2+s−s̄ , damit auch |yi − y| ≤ 2ε−2+s .
Zu einem gegebenen y ∈ I können wir also (in konstanter Zeit!) ein i mit |y i − y| ≤ 2ε−1+s finden. Dann ist
|xi − f (y)| = |f (xi ) − y|/|f 0 (ξ)| ≤ 2−s (2ε−1+s + 2ε−2+s ) < 2ε (für ein geeignetes ξ ∈ [a, b]). Also ist x i als
Startwert für die Fixpunktbestimmung bei dem gegebenen Wert y verwendbar.
59
5 INTERVALL-ARITHMETIK
5
Intervall-Arithmetik
Stichworte: Intervall-Methoden, Fehlerkontrolle, Wachstum der Intervalle
In diesem Kapitel betrachten wir die grundlegenden Begriffe der Intervall-Arithmetik, mit der die aufwendigen Fehlerabschätzungen des letzten Kapitels zumindest teilweise automatisiert werden können. Als bahnbrechende Arbeit
auf diesem Gebiet gilt [Mo62], erste verwandte Arbeiten gehen jedoch wohl schon auf die Jahre 1924 und 1932 zurück, wie z.B. in [Ke96] angemerkt (ein Preprint dieser Arbeit in PDF-Form findet sich unter
interval.louisiana.edu/preprints/survey.pdf .
Im
WWW
finden
sich
umfangreiche
Literatur-Sammlungen
zu
diesem
Gebiet,
z.B. liinwww.ira.uka.de/bibliography/Math/intarith.html , in der über 2000 Artikel aufgenommen wurden. Eine sehr
ergiebige Quelle ist auch www.cs.utep.edu/interval-comp/.
Bei den Algorithmen des letzten Kapitels haben wir immer versucht, den durch Rundungen entstehenden Fehler
möglichst genau abzuschätzen. Eigentlich haben wir damit stets mit Zahlenpaaren (x, ε) an Stelle eigenlich gemeinter einzelner Zahlen y gerechnet, wobei ε so gewählt war, dass |x − y| ≤ ε, also x − ε ≤ y ≤ x + ε
galt. Bei jeder arithmetischen Operation f auf einem (gegebenen) x und einem (gedachten) ε wurde darauf geachtet, dass für das (explizit berechnete) Resultat x 0 und den (nur im jeweiligen Beweis nachvollzogene) ε 0 galt
y ∈ [x − ε, x + ε] ⇒ f (y) ∈ [x0 − ε0 , x0 + ε0 ].
Besonders lästig war dabei, dass diese Eigenschaft von Programmierer für jede einzelne Operation gesondert sichergestellt werden musste. Eine Alternative ist es, diese Abbildung (x, ε) 7→ (x 0 , ε0 ) von Computer durchführen zu
lassen. Dies bedeutet zwar einerseits, dass statt x nun auch die Fehler-Information ε zu speichern ist, ermöglicht
aber andererseits automatisierte verläßliche Berechnungen (reliable computing). Die Basis bildet hier also IntervallArithmetik. Im Folgenden betrachten wir die grundlegenden Begriffe, wie sie beispielsweise in [Ke96] definiert werden: Wir werden hier nur den Fall reeller Intervalle betrachten, oft werden jedoch auch Intervalle komplexer Zahlen
betrachtet.
5.1 Definition. := { [a, b] | a, b ∈ , a ≤ b} sei die Menge aller (abgeschlossenen) reellen Intervalle.
Intervalle der Form [a, b] mit a < b nennt man echte Intervalle, im Falle a = b spricht man von Punkt-Intervallen.
Ein Intervall-Vektor ist ein Tupel I = (I 1 , . . . , In ) mit Ii ∈ . Er bezeichnet das Kreuzprodukt I 1 × . . . × In ⊆ n
der Einzel-Intervalle.
2
Zur Implementierung von Intervallen gibt es im Wesentlichen zwei Methoden: Es werden wie in der obigen Definition linke Grenze a und rechte Grenze b gespeichert oder aber Mittelpunkt x und Radius ε der Intervalle. Prinzipiell
sind beide Zugangsweisen äquivalent, allerdings legen die gerichteten Rundungsmodi der Fließkommaarithmetik die
Implementierung über [a, b] nahe, so daß wir in diesem Kapitel auch diese Form der Intervalle verwenden werden.
Im darauf folgenden Kapitel werden wir jedoch auch noch die Form x ± ε genau untersuchen.
Zunächst werden wir exakte Arithmetik auf den reellen Zahlen voraussetzen und auch beliebige reelle Grenzen a i , bi
erlauben.
Eine wichtige Grundlage der Intervallarithmetik bildet das folgende einfache Lemma:
5.2 Lemma. Es sei f eine Funktion, die auf einem Intervall [a, b] stetig ist. Dann ist f [a, b] := {f (x) | x ∈ [a, b]}
ebenfalls ein Intervall, d.h. es gibt a 0 , b0 mit [a0 , b0 ] = f [a, b]. Dabei ist a0 = min{f (x) | x ∈ [a, b]} und b0 =
max{f (x) | x ∈ [a, b]}
2
Beweis: Die Behauptung gilt offensichtlich, da jede stetige Funktion auf einer kompakten Menge sowohl Maximum
als auch Minimum annimmt. Auch bei mehrstelligen Funktionen ist das Ergebnis wieder ein Intervall.
5.3 Definition. Eine Funktion F : gibt mit F (I) = {f (x) | x ∈ I}.
n
→ heißt Intervall-Funktion, wenn es eine stetige Funktion f : 60
n
→
2
5 INTERVALL-ARITHMETIK
5.4 Korollar. Jede stetige Funktion f : F : n → erweitert werden.
n
→ kann durch F (I) := {f (x) | x ∈ I} zu einer Intervall-Funktion
2
5.5 Lemma. Es seien I1 = [a1 , b1 ] und I2 = [a2 , b2 ] aus gegeben. Für die aus den elementaren arithmetischen
Operationen abgeleiteten Intervall-Funktionen gilt dann
I1 + I2 = [a1 + a2 , b1 + b2 ]
I1 − I2 = [a1 − b2 , a2 − b1 ]
I1 · I2 = [min{ai · bi | i, j ∈ {1, 2}}, max{ai · bi | i, j ∈ {1, 2}}]
I1 /I2 = [min{ai /bi | i, j ∈ {1, 2}}, max{ai /bi | i, j ∈ {1, 2}}]
Im Fall I1 /I2 muß dabei allerdings 0 6∈ I2 gelten. Für den Durchschnitt von Intervallen gilt
I1 ∩ I2 = [max{a1 , b1 }, min{a2 , b2 }]
2
Einige wichtige Eigenschaften der elementaren Intervall-Arithmetik sind:
5.6 Lemma. Kommutativgesetz:
I1 + I 2 = I 2 + I 1 , I 1 · I 2 = I 2 · I 1
Assoziativgesetz:
(I1 + I2 ) + I3 = I1 + (I2 + I3 ) , (I1 · I2 ) · I3 = I1 · (I2 · I3 )
Neutrale Elemente:
[0, 0] + I = I , [1, 1] · I = I
Subdistributivität:
I1 · (I2 + I3 )
⊆
I 1 · I2 + I1 · I3
Inklusionsmonotonie:
I1 ⊆ I 3 , I 2 ⊆ I 4
⇒
I1 ◦ I2 ⊆ I3 ◦ I4
2
Anmerkung: Bei echten Intervallen gibt es keine additiven und multiplikativen Inversen, ferner gilt das ‘normale’
Distributivitätsgesetz nicht.
Bei den einfachen arithmetischen Operationen wird der Wertebereich beim Übergang auf Intervalle exakt eingehalten. Überträgt man jedoch Ausdrücke auf Zahlen in direkter Weise in Ausdrücke auf Intervallen, so enstehen
Intervalle, die im Allgemeinen umfangreicher als notwendig sind:
Für die Funktion f (x) = x − x erhalten wir beispielsweise:
f ([0, 1]) = [0, 0],
[0, 1] − [0, 1] = [−1, 1]
Bei g(x) = x·(1−x) ergibt sich g([0, 1]) = [0, 0.25], aber je nach Art der Auswertung des Ausdrucks über Intervalle
entstehen verschiedene Intervall-Resultate:
g(x) = x · (1 − x) : [0, 1] · ([1, 1] − [0, 1]) = [0, 1] · [0, 1] = [0, 1]
g(x) = x − x · x : [0, 1] − [0, 1] · [0, 1] = [0, 1] − [0, 1] = [−1, 1]
Bei Intervallen, die 0 enthalten, ist es auch sinnvoll, Potenzen der Form x n als Elementar-Intervall-Operation zu
verwenden und sie nicht auf iterierte Intervall-Multiplikationen zurückzuführen: So ergibt sich für x 2 bei a < 0 < b
61
5 INTERVALL-ARITHMETIK
das ‘quadrierte’ Intervall [a, b]2 = [0, max(a2 , b2 )], also etwa [−1, 1]2 = [0, 1] im Vergleich zu [−1, 1] · [−1, 1] =
[−1, 1].
Der Grund für dieses ‘Aufblähen’ der Intervalle liegt darin, dass bei der Auswertung über die Ausdrücke nicht
berücksichtigt werden kann, dass die Parameter voneinander abhängig sind.
Vorteile bringt oft die Kombination von Intervall-Arithmetik zusammen mit dem Mittelwertsatz der Analysis: Betrachten wir wieder die Funktion g(x) = x − x 2 , so ergibt sich hier g(x) = g(0.5) + g 0 (ξ) · (x − 0.5), beim Intervall
[0, 1] also mit ξ ∈ [0, 1]:
g([0, 1]) = g(0.5) + (1 − 2 · [0, 1]) · [−0.5, 0.5] = [−0.25, 0.75]
Da für g([0, 1]) alle drei angegebenen Intervalle [−1, 1], [0, 1] und [−0.25, 0.75] gültig sein müssen, dürften wir sogar
jetzt den Durchschnitt [0, 0.75] der Intervalle verwenden.
Bei praktischen Anwendungen werden meist Fließkommazahlen statt exakter reeller Arithmetik benutzt. Achtet man
dabei darauf, immer die entsprechende gerichtete Rundungen nach +∞ oder −∞ zu verwenden, so entstehen auch
dabei Intervalle, die das gesuchte Ergebnis korrekt einschließen. Problematisch ist dabei, dass Hochsprachen oft nicht
zulassen, den Rundungsmodus der Hardware-Arithmetik zu beeinflussen. Hardware-orientierte Intervall-ArithmetikPakete müssen daher i.A. an kritischen Punkten auf Assembler-Sprachen zurückgreifen.
5.7 Definition. Der Betrag eines Intervalles I ∈ ist |I| := max{|x| | x ∈ I}.
Der Abstand zweier Intervalle I1 = [a1 , b1 ] und I2 = [a2 , b2 ] ist q(I1 , I2 ) := max(|a1 − a2 |, |b1 − b2 |). Für Vektoren
I = (I1 , . . . , In ) und J = (J1 , . . . , Jn ) definieren wir q(I, J ) := max{q(Ii , Ji ) | 1 ≤ i ≤ n}.
Der Durchmesser eines Intervalles I = [a, b] ∈ ist d(I) := b − a.
5.8 Lemma. Der Abstand q : Int( ) n × ungleichung:
n
2
→ ist eine Metrik, d.h. es gelten Positivität, Symmetrie und Dreiecks-
Positivität:
q(I, J) ≥ 0 , q(I, J) = 0 ↔ I = J
Symmetrie:
q(I, J) = q(J, I)
Dreiecksungleichung
q(I, J ) ≤ q(I, K) + q(K, J )
2
Unter Verwendung dieser Metrik kann man auf Begriffe wie ‘offene Mengen’, ‘Konvergenz’ und ‘Stetigkeit’
definieren.
Mit Hilfe der Intervall-Arithmetik kann unter Umständen die Existenz von Lösungen von Gleichungen nachgewiesen
werden. Basis dafür ist oft der Brouwer’sche Fixpunkt-Satz [Bro12]:
5.9 Satz. Ist D homeomorph zur abgeschlossenen Einheitskugel in D, so hat P einen Fixpunkt x in D, d.h. P (x) = x.
n
und ist P eine stetige Abbildung mit P (D) ⊆
2
Transformieren wir z.B. eine stetige Funktion φ : n → n in eine Intervall-Fassung Φ : n → n und erhalten
Φ(I) ⊆ I, so muß es in I einen Fixpunkt von φ geben. Als Beispiel betrachten wir eine Intervall-Newton-Methode
zu Bestimmung von Nullstellen:
62
5 INTERVALL-ARITHMETIK
5.10 Beispiel.Hat eine Funktion f : [a, b] → eine stetige erste Ableitung f 0 auf [a, b] und ist F eine IntervallFunktion, die f 0 auswertet (nicht notwendigerweise mit minimaler Intervall-Größe), so nennt man
N (f ; [a, b], x) := x − f (x)/F ([a, b])
eine univariate Intervall-Newton-Methode.
Findet man Werte [a, b] und x ∈ [a, b] mit N (f ; [a, b], x) ⊆ [a, b], so gibt es ein eindeutig bestimmtes y ∈ [a, b]
mit f (y) = 0. Über die quadratische Konvergenz ergibt sich, dass bei ausreichend kleinem Startintervall [a, b] keine
Aufblähung der Intervalle stattfindet, sondern sie sogar kleiner werden können. Dann kann man auch evtl. auf die
Anwendung von Software-Fließkomma-Arithmetik wie etwas MPFR verzichten.
2
Eine automatische Umsetzung normaler arithmetischer Algorithmen auf Intervall-Form ist leider nicht einfach,
Hauptproblem bilden dabei die Vergleichsoperatoren: Ein Größen-Vergleich zweier Intervalle I = [a 1 , b1 ] und J =
[a2 , b2 ] ist nur dann möglich, wenn sich die Intervalle nicht überlappen. Sollten die Intervallen jedoch einen nichtleeren Durchschnitt haben, so müßte man bei einem Programmstück der Form if I < J then A else B
sowohl A als auch B auswerten und die Vereinigung der entstehenden Intervalle bilden. Dies ist jedoch nicht immer
erfolgreich möglich, da z.B. bei A die Bedingung I < J nicht gelten muß.
Zum Abschluß dieses Abschnittes sei noch auf einige Erweiterungen der Intervall-Arithmetik hingewiesen, bei denen z.B. sinnvoll unendlich große Intervalle, entstanden durch Division durch 0, verwendet werden können. Bei
der Kahan-Novoa-Ratz-Arithmetik, vgl. [Ke96], modifiziert man z.B. die Intervall-Division und setzt hier z.B.
[1, 2]/[−1, 1] := [−∞, −1] ∪ [+1, ∞]
63
Herunterladen