Vorherige Seite Nächste Seite Inhalt

3. Implementation

Ich habe eine Programmbibliothek geschrieben, die Funktionen enthält, um lange Zahlen zu addieren, subtrahieren und zu multiplizieren. Ein Beispielprogramm zeigt, wie man diese Funktionen verwendet.

mpi.zip

Komplettes Paket (ZIP-File)

mpi.cpp

Programmbibliothek

mpi.h

Headerdatei der Bibliothek

main.cpp

Beispielprogramm, das die meisten Funktionen verwendet

Da wir es gewohnt sind im Dezimalsystem zu rechnen, verwendet diese Programmbibliothek auch diese Darstellung, damit man die Schritte besser nachvollziehen kann. Für einen Rechner wäre das Binärsystem besser geeignet, und wird auch in den erhältlichen Programmbibliotheken verwendet. Wenn bei einer arithmetischen Operation der gültige Zahlenbereich verlassen wird, so wird ein Übertrag gesetzt, der bei weiteren arithmetischen Operationen automatisch mit einbezogen werden kann. Es sind dann bei der Berechnung keine Zeitaufwendigen Abfragen und Sprünge notwendig, um zu überprüfen, ob der Zahlenbereich verlassen wurde.

In dieser Programmbibliothek werden folgende arithmetische Operatoren aufgeführt:

3.1 Ablegen der langen Zahl im Speicher

Um die langen Zahlen zu speichern, wird ein Speicherbereich reserviert, in dem die einzelnen Ziffern hintereinander abgelegt werden. Ziffer0 ist die erste, niederwertigste Stelle, und Ziffern ist die n+1ste Stelle. Die Ziffer kann also folgendermaßen dargestellt werden: Ziffern*10^n. Ein Parameter "lang", zeigt an, wieviel Ziffern in dem Speicherbereich abgelegt werden können. Ist dieser Parameter negativ, so deutet dies auf einen Fehler hin. Dies geschieht, wenn nicht mehr genug Speicher vorhanden ist, bzw. wenn bei einer Berechnung ein Fehler auftritt (z.B. bei Subtraktion einer größeren Zahl von einer kleineren). Es sind Routinen vorhanden, um diesen Speicher zu reservieren: mpi_getmem(n), und um ihn wieder freizugeben: mpi_freemem(..), wenn die Zahl nicht mehr benötigt wird.

Bei der Addition wird für das Ergebnis ein Speicherbereich reserviert, der um eine Stelle größer ist als die größere der beiden zu addierenden Zahlen. Bei der Multiplikation werden die Anzahl der Stellen der beiden Zahlen addiert, um den Speicherbedarf des Ergebnisses zu ermitteln.

3.2 Addition

Die Addition wird wie oben im Beispiel aufgeführt durchgeführt.


Struktogramm Addition

3.3 Subtraktion

Die Subtraktion ist der Addition sehr ähnlich. Ist die zu subtrahierende Ziffer größer, so wird eine eins von der nächst höheren Ziffer "geborgt".


Struktogramm Subtraktion

3.4 Multiplikation

Vorüberlegung

Beispiel einer Multiplikation, wie wir sie von Hand durchführen würden:

1111*1234

Wir würden dies mit einer Reihe von einfachen Multiplikationen und Additionen lösen.

       0
+   4444        (ist 4*1111 um 0 Stellen verschoben)
+  33330        (ist 3*1111 um 1 Stellen verschoben)
+ 222200        (ist 2*1111 um 2 Stellen verschoben)
+1111000        (ist 1*1111 um 3 Stellen verschoben)
========
 1370974

Implementation

Bei der Multiplikation wird zuerst die eine Zahl mit jeweils den Ziffern 0 bis 9 multipliziert, und das Ergebnis zwischengespeichert. Dies wird mit mpi_mulkurz() durchgeführt. Da wir sehr lange Zahlen haben, müßte man bei 200 Stellen auch 200 Multiplikationen durchführen. Hiervon würden sehr viele Multiplikationen mehrfach ausgeführt werden, da es nur zehn Ziffern gibt. Diese möglichen Multiplikationen kann man bereits vorher durchführen um Zeit zu sparen. Anschließend braucht man die berechneten Zahlen nur noch stellenrichtig zu addieren. Dies wird von der Routine mpi_addiere_free() ausgeführt. Es wird bei der niedrigsten Stelle begonnen, und je nachdem was für eine Ziffer dies ist (0 bis 9), wird die vorberechnete Multiplikation für diese Ziffer bei der Addition verwendet. Zusätzlich wird die vorberechnete Zahl um n Stellen verschoben, wenn die n-te Stelle berechnet wird.


Struktogramm Multiplikation

3.5 Ausblick

Eine Division kann durch mehrfaches Subtrahieren gelöst werden. Die Anzahl der nötigen Subtraktionen liefert das Ergebnis der Division. Der Rest, der bei einer Ganzzahl Division anfällt, ist der Modulo. Um die Division zu beschleunigen, kann man z.B. Zehnerpotenzen des Nenners vom Zähler abziehen, sofern dieser dadurch noch kleiner ist als der Zähler.

3.6 Anmerkung zum Beispielprogramm main.cpp

Es wird bei der Darstellung die niederwertigste Ziffer links ausgegeben, und die höherwertigen Ziffern weiter rechts. Also 00315062 im Programm ist in der üblichen Schreibweise 26051300. Es wurden beim testen auch Berechnungen mit bis zu 17000 Stellen durchgeführt. Bei mehr Stellen kann es je nach eingestelltem Speichermodell, Betriebssystem und Rechnerarchitektur zu Fehlern kommen. Speziell bei einer Multiplikation wird sehr viel Speicher benötigt. Hier sollte der erste Parameter die kürzere Zahl enthalten, da dann weniger Speicher benötigt wird.

Beispiel Berechnung:

788539152479959923583473870729725158796647538883718863262181413347391236469
x
163117845256502920687558543656697020247411364462120385893160945804553250187472
604743326476435522680378897
=
012862480745292006410890272858478665047052244641713578041943468146981010437930
160098645877829367241942440036761175181846713094902521848685430625024876210479
4478806532579114244394693

Die ersten beiden Zahlen sind in den Dateien test1.txt und test2.txt abgelegt. In dem Programm main.cpp werden zwei zufällige Zahlen addiert, subbtrahiert und multipliziert. Anschliessend wird die für 100 Multiplikationen benötigte Zeit gemessen. Danach wird die oben aufgeführte Beispiel Berechnung durchgeführt. Das Ergebnis wird in der Datei test3.txt abgelegt. Beim Lesen und Schreiben der Dateien werden die Zahlen in das richtige Format gewandelt. Zahlen <--> ASCII-Zeichen und Darstellung 0001 <--> 1000.

3.7 Zeiten

Das Programm wurde unter DOS Borland C 3.1 mit dem Speichermodell Small compiliert. Wird ein anderes Speichermodell gewählt, bei dem für die Daten FAR Zeiger verwendet werden, dann dauert die Berechnung länger. Es wurde 100 mal eine Multiplikation mit jeweils zwei 1000 stelligen Zahlen ausgeführt. Dabei benötigt ein Pentium 90 ca. 80s, ein Pentium 200 benötigt hierfür 38s.


Vorherige Seite Nächste Seite Inhalt