Schriftliche Division:
Erst mal ein Beispiel:
54321 : 25 = 2172
50
--
43
25
--
182
175
---
71
50
--
21
Dann der allgemeine Fall:
Algorithmus für die Ganzzahl-Division zweier "langer" Zahlen
Sei der erste Dividend eine Zahl a mit der Ziffernfolge (ak, ak-1, ak-2, ...., a1, a0)
und der Divisor eine Zahl b mit der Ziffernfolge (bn, bn-1, ...., b1, b0).
Dann erhält man a DIV b und a MOD b, indem man folgendermaßen vorgeht:
Es seien r, p und q drei (lange) Hilfszahlen, d eine Integerzahl und i ein Index.
Setze q auf Null.
Wenn a mindestens soviele Stellen hat wie b, dann:
lege in r die aus den ersten n Stellen von a gebildete Zahl ab;
setze i auf (k-n), dies ist der Index der nächsten noch nicht bearbeiteten Stelle von a.
andernfalls (also: a hat echt weniger Stellen als b):
setze r auf den Wert von a;
setze i auf (-1);
Solange i >= 0 ist wiederhole:
{
Schiebe r um eine Stelle nach links und addiere die Ziffer a[i].
Bestimme, wie oft b in r enthalten ist und lege das Ergebnis in d ab.
Verschiebe q um eine Stelle nach links und addiere dann d.
Berechne das Produkt aus b und der Ziffer q[0] und lege es in p ab.
Vermindere r um p.
Setze i auf i - 1.
}
Das Ergebnis der Division steht nun in q, der Rest in r.
Bemerkungen:
- Beachten Sie, dass r zunächst mit einer Stelle "zuwenig" initialisiert wird! Die fehlende Stelle wird dann erst in der Schleife hinzugefügt. Die Alternative ist eine "vollständige" Initialisierung außerhalb der Schleife und das Hinzufügen der nächsten Stelle am Ende der Schleife. Dies führt jedoch beim letzten Schleifendurchlauf zu einem Zugriff auf a[-1], was nicht erlaubt ist und daher durch eine Fallunterscheidung abgefangen werden müsste.
- Es ist möglich, dass die führende Ziffer im Ergebnis q eine Null ist. Denken Sie sich ein Beispiel aus, das dieses zeigt! Man muss also in q noch eventuelle führende Nullen entfernen.
- Beachten Sie, dass der Algorithmus wörtlich genau so für alle möglichen Stellenwert-Systeme gilt, also z.B. auch für das Zweier-System oder das 16er-System oder sonst eines!
Hilfsprozedur:
Algorithmus für "Wie oft ist b in r enthalten?"
Es ist nun noch zu klären, wie wir ermitteln, wie oft man eine lange Zahl b in einer langen Zahl r enthalten ist, wenn wir schon wissen, dass das Ergebnis nur aus einer Ziffer z besteht. (Dieser Rechenschritt ist analog zu dem bei der Multiplikation benutzten Spezialfall "MultInt".)
Im Hinblick auf spätere Erweiterungen unserer Algorithmen schlage ich vor, nach folgender Grundidee vorzugehen:
- Man bestimme aus der Serie der Zahlen b, 2b, 4b, 8b,... (also 2kb) die größte Zahl, die noch kleiner oder gleich r ist. Wenn diese Zahl 2m ist, dann ist also b in r mindestens 2m mal enthalten, aber nicht 2m+1-mal! Also ist z >= 2m, und wir setzen zunächst z auf diesen Wert.
- Nun subtrahiert man von r die Zahl 2mb und bestimmt für das neue r wieder die größte ganze Zahl aus der obigen Serie, die kleiner oder gleich r ist. Sei dies nun 2nb, dann ist b also insgesamt mindestens (2m+2n) mal im (ursprünglichen!) r enthalten. Daher addieren wir zu z den Wert 2n.
- Wiederholen wir den vorigen Schritt, bis r < b ist, dann enthält z die gesuchte Anzahl.
Bemerkungen:
- Dieser Algorithmus konstruiert den Wert von z formal über seine Binärdarstellung. Daraus folgt, dass man den Algorithmus optimieren könnte, wenn man gleich komplett im Binärsystem rechnen würde. Das wollen wir hier aber nicht.
- Der Algorithmus lässt sich besonders elegant mit Hilfe einer rekursiven Funktion formulieren. Es geht natürlich auch "normal"....
- Der letzte Wert von r enthält den Rest, den der ursprüngliche Wert von r bei der Ganzzahldivision durch b lässt. Diese Tatsache können Sie bei der Implementierung des obigen Algorithmus' der schriftlichen Division zur Optimierung einsetzen, um einigen Rechenaufwand zu sparen.
- Eine weitere Optimierungs-Idee ist, die Zahlen 2kb in einer verkürzten "Vielfachen-Liste" zwischenzuspeichern.
- Beachten Sie aber bei allen Optimierungsversuchen, dass Zuverlässigkeit und Wartbarkeit in der Regel höher zu bewerten sind als Schnelligkeit!
In der Praxis verursachen hochoptimierte Programmteile einen deutlich höheren Dokumentationsaufwand als "normale". Wer dies nicht berücksichtigt und sich die genaue Dokumentation ersparen will, wird morgen das Programm nicht mehr verstehen, das er heute schreibt.
Roland Mechling, 16.02.2005