Ein schneller Quadrierungs - Algorithmus für Zahlensysteme mit beliebiger Basis
Übersicht
Hier wird der Panflöten-Algorithmus vorgestellt, der die Quadrierung einer Zahl mit dem halben Aufwand leistet
wie der übliche Multiplikations-Algorithmus.
Er funktioniert bei b-adischen Zahlensystemen mit beliebiger Basis.
Bei gebrochenen Zahlen entfernt man zunächst das Komma
und markiert beim Ergebnis die doppelte Anzahl Nachkommastellen.
Der Algorithmus binär
Die binäre Version ist - wie bei allen Algorithmen - besonders einfach.
Die zu quadrierende Zahl N bestehe aus dem Ziffernstring nmax ... n1n0
(in der symbolischen Darstellung links aus den Ziffern abcd)
Beispiel: Quadriere 15 = 11112:
a_b_c_d 1_1_1_1 Zeile 1: Zahl gespreizt bcd 111 Zeile 2 cd 11 Zeile 3 d 1 Zeile 4 ------- -------- Summe 11100001 Zeile 5: Summe = Ergebnis - Zeile 1: Schreibe die zu quadrierende Zahl gepreizt hin. Statt Nullen fügt man Unterstriche ein (übersichtlicher) - Zeilen 2, 3, 4: Für i = imax herunter bis 1: Falls Ziffer ni ≠ 0 : Schreibe unter Ziffer ni den Ziffernstring ni-1 ... n0 - Zeile 5: Summiere alles (Zeilen 1 bis 4) = Ergebnis
Es folgen noch 2 Beispiele mit Nullen in der Zahl. Hier sieht man, daß beim Spreizen der Zahl Unterstriche statt Nullen übersichtlicher sind, und daß unter eine Ziffer 0 der gespreizten Zahl kein Ziffernstring geschrieben wird. Führende Nullen in einem Ziffernstring sollten geschrieben werden, damit dieser genau unter der entsprechenden Ziffer der gespreizten Zahl beginnt (optische und didaktische Klarheit). Beispiele: 23 = 101112 und 54 = 1101102 :
1_0_1_1_1 1_1_0_1_1_0 0111 10110 11 0110 1 10 --------- 0 1000010001 ----------- 101101100100
Damit nicht nur der Rechenaufwand, sondern auch der Platzbedarf halbiert wird, kann man eine komprimierte Schreibweise anwenden: Man schreibt außer dem 1. Ziffernstring noch weitere Ziffernstrings in die 2. Zeile (oder eine andere), wenn dort Platz ist, was spätestens nach Abarbeiten der (abgerundet) halben Ziffernzahl der Fall ist. Obige Beispiele komprimiert:
1_0_1_1_1 1_1_0_1_1_0 0111111 10110 100 --------- 0110 1000010001 ----------- 101101100100
Für Anfänger ist die komprimierte Schreibweise nicht zu empfehlen,
und auch Geübte sollten Ziffernstrings,
die nicht durch eine Lücke getrennt sind, durch ein Komma o.Ä. trennen.
Zum dezimalen Quadrierungs-Algorithmus
Vereinfachung bei Einserblocks
Eine Folge von x Einsen in der zu quadrierenden Zahl würde nach obigem Algorithmus
x zu addierende Teilstrings der Zahl erzeugen. Es geht jedoch einfacher.
Beispiel: Quadriere 2037 = 11111110101
1_1_1_1_1_1_1_0_1_0_1 Zahl gespreizt, streiche Einsen 1111110101 erster Teilstring 1 nach links -0101 -letzter Teilstring minus 01 ---------------------- 1111110101000001111001 Summe = Ergebnis
Der Einserblock erfährt eine Sonderbehandlung:
-- Schreibe den durch die erste 1 erzeugten Teilstring um 1 weiter links
-- (alle inneren Einsen des Blocks erzeugen nichts)
-- setze vor den durch die letzte 1 erzeugten Teilstring ein Minuszeichen
(incl. Minuszeichen beginnt also auch dieser String um 1 weiter links)
-- streiche in der gespreizten Zahl alle Einsen des Blocks außer der ganz rechts
(d.h. diese werden nicht addiert)
Diese Vereinfachung kann man auf jeden Einserblock in einer Zahl anwenden -
oder nur auf einige: Die Zahl der zu addierenden / subtrahierenden Ziffern ist zwar schon
bei dem Block 11 geringer, aber der Algoritmus ist etwas komplizierter (Addition und Subtraktion).
Ab dem Block 111 lohnt sich die Vereinfachung sicher. Bei obigem Beispiel mit einem Block aus 7 Einsen
werden nur 2 statt 7 Teilstrings erzeugt und verrechnet
Diese Methode ähnelt dem Booth-Algorithmus für 1-er Folgen bei der binären Multiplikation
Besonders einfach ist so die Quadrierung einer Zahl, die nur aus einem Einserblock besteht.
Quadriere 15 = 11112:
1_1_1_11_1_1_1 111 11100001 -------- 11100001 komprimiert
Der durch die letzte Eins erzeugte, zu subtrahierende Teilstring ist hier leer, man schreibt nichts.
Man addiert nur 2 Dinge:
- den durch die erste 1 erzeugten Teilstring (um 1 nach links verschoben!)
- die rechteste 1 der gespreizten Zahl
Einfacher formuliert: Um eine Binärzahl zu quadrieren, die nur aus x Einsen besteht,
fügt man vor der letzten Eins x Nullen ein. Die Quadratzahl besteht somit aus x Einsen und x Nullen.
Noch einfacher ist die bekannte Regel, wie man eine runde Zahl (1 gefolgt von x Nullen)
in einem beliebigen "normalen" Zahlensystem quadriert: Man verdoppelt einfach die Anzahl der Nullen.
Man kann auch hier eine Art komprimierte Schreibweise anwenden (Bild rechts):
Man schreibt die rechte 1 nochmal in Zeile 2, füllt den Zwischenraum mit Nullen (rot) und hat schon das Ergebnis
Herleitung
Wir zeigen an einem Beispiel die theoretische Grundlage. Gegeben sei eine 4-stellige natürliche Zahl N zur Basis b:
N = n3n2n1n0
= ( n3*b3 + n2*b2 +
n1*b1 + n0*b0 )
Will man diese Zahl quadrieren, muss man - nach den üblichen Klammerregeln -
jedes Element der Klammer mit jedem Element der Klammer (incl. sich selbst) multiplizieren.
Nach Umsortieren und geeignetem Zusammenfassen erhält man:
N2 = n32*b6 + n22*b4 + n12*b2 + n02*b0 Jede Ziffer samt Stellenwert quadriert
+ 2n3 * ( n2*b5 + n1*b4 + n0*b3 )
+ 2n2 * ( n1*b3 + n0*b2 )
+ 2n1 * ( n0*b1 )
Dieses Schema funktioniert bei mehr Ziffern genauso: Hat man 1 Ziffer mehr, so müßte man statt n2
Produkten von je 2 Ziffern (n=Ziffernzahl) jetzt (n+1)2 = n2 + 2n + 1 Produkte von je 2 Ziffern haben.
Und die hat man, neu hinzu kommen nämlich 1 neue quadrierte Ziffer (in obiger Formel nach dem Gleichheitszeichen einzufügen)
und ihre 2n Produkte mit jeder anderen Ziffer (neue Zeile nach der ersten einfügen, analog zur 2. Zeile).
Der binäre Algorihmus läßt sich noch weiter vereinfachen:
- Es gibt nur die Ziffern 0 und 1, und es gilt 0x = 0 und 1x = 1
Man kann also in der oberen Zeile bei den Ziffern n3 bis
n0 die Exponenten 2 weglassen
- Dann kann man die obere Zeile zu einer Zahl (N gespreizt) zusammenfassen
(die Exponenten bi werden durch die Stellenwerte ausgedrückt), also:
n3 0 n2 0 n1 0 n0 (7-stellige Zahl mit 3 Nullen)
- Auch die Ausdrücke in Klammern kann man zu je einer Zahl zusammenfassen
(ergibt verschieden lange Teilstrings von N) und dann noch
den Faktor 2 durch eine Erhöhung um 1 der Potenz von b ausdrücken, also:
+ n3 * ( n2n1n0*b4 ) [ aus 2n3 * ( n2n1n0*b3 ) ]
+ n2 * ( n1n0*b3 ) [ aus 2n2 * ( n1n0*b2 ) ]
+ n1 * ( n0*b2 )
[ aus 2n1 * ( n0*b1 ) ]
Die Multiplikation n3 * ( n2n1n0*b4 )
z.B. bedeutet gerade, daß der Ziffernstring n2n1n0 an die durch b4
bestimmte Stelle geschrieben wird, wenn n3 =1 ist, andernfalls nicht (weil das Ergebnis 0 ist)
Auch die Vereinfachung bei Einserblocks läßt sich leicht herleiten:
Wir berechnen das Quadrat der Zahl und vereinfachen schrittweise die Struktur des Rechenschemas.
Obiges Beispiel: Quadriere 2037 = 11111110101
Schritt 0: Berechne das Quadrat ohne Vereinfachung. Die durch den Einserblock erzeugten
7 Strings sind farblich unterteilt, um die Umgruppierung beim nächsten Schritt klarer zu machen:
1_1_1_1_1_1_1_0_1_0_1 1111110101 111110101 11110101 1110101 110101 10101 0101 01 ---------------------- 1111110101000001111001
Schritt 1: Die Einsen des Einerblocks in jedem Teilstring werden anders gruppiert. Man erkennt das System: Je zwei Einsen (und zuletzt eine Rest-Eins) ergeben einen fortlaufenden Einser-String. Die Einser-Srings werden links und rechts um je 2 Stellen kürzer:
111 1111111 11111111111
Schritt 2: Jede Zeile von x Einsen wird durch den Ausdruck 2x-1 ersetzt (z.B. 111 durch 23-1 = 1000-1). Zur Vereinfachung lassen wir die Nullen weg, schreiben aber jede 1 und -1 natürlich stellenrichtig (d.h. jede positive 1 um eine Stelle weiter nach links als die erste 1 des Blocks):
1 -1 1 -1 1 -1
Schritt 3: Wir fassen alle 1 und -1 des obigen Schemas in 1 Zeile zusammen:
1 1 1 -1-1-1
Schritt 4: Die Reste 0101 der Teilstrings sind um je 1 Stelle verschoben, ergeben also den Wert 0101*1111111. Dieser Ausdruck wird ersetzt durch 0101*(10000000-1), was 01010000000-1 ergibt. Zur Vereinfachung lassen wir die Nullen weg, schreiben aber die 0101 und -0101 natürlich stellenrichtig (d.h. die 0101 um eine Stelle weiter nach links als die erste 0101 des Blocks):
0101 -0101
Schritt 5: Wir ersetzen im Gesamtschema (Schritt 0) die 7 durch den Einserblock erzeugten Teilstrings durch die in Schritt 2 und 4 gewonnenen Ausdrücke. (Der schwarze Teilstring 01 hat mit dem Einserblock und dessen Vereinfachung nichts zu tun, sondern wird durch die zweitletzte 1 der gespreizten Zahl erzeugt):
1_1_1_1_1_1_1_0_1_0_1 1 1 1 -1-1-1 0101 -0101 01 ---------------------- 1111110101000001111001
Schritt 6: Wir verrechnen den grünen Teilstring mit der gespreizten Zahl
und schreiben den roten Teilstring in dieselbe Zeile. Nun haben wir gerade das vereinfachte Schema.
(Der negative Teilstring, hier -0101, überlappt sich manchmal mit dem positiven Teilstring,
abhängig von der zu quadrierenden Zahl, und muß dann in die nächste Zeile geschrieben werden):
1_1_1_1_1_1_1_0_1_0_1 1111110101 -0101 01 ---------------------- 1111110101000001111001
Aufwand
- Bei Quadrierung mit dem üblichen Multiplikationsalgorithmus müssen bei einer Zahl
mit n Stellen maximal n Zahlen mit je n Einsen addiert werden, also insgesamt n2 Ziffern
- Bei Quadrierung mit dem hier gezeigten Algorithmus müssen die gepreizte Zahl mit max. n Einsen
sowie maximal n-1 Zahlen mit durchschnittlich (n-1)/2 Ziffern addiert werden.
Zusammen also (n-1)2/2 + n = (n2 - 2n + 1 + 2n) /2 also etwa n2/2 Ziffern
Man hat also nur den halben Aufwand
(Durch die oben beschriebene Vereinfachung bei 1-er Blocks kann der absolute Aufwand sehr viel geringer werden,
aber das gilt auch für die übliche binäre Mutliplikation mit Vereinfachung durch den Booth-Algorithmus)
Potenzierung
Der schnelle Quadrierungs-Algorithmus macht auch die Berechnung höherer Potenzen schneller.
Denn es ist üblich, z.B. zur Berechnung von N8 so vorzugehen:
- Berechne N2
- Berechne N4 aus N2 * N2
- Berechne N8 aus N4 * N4
Allgemein setzt man jede beliebige Potenz aus kleineren Potenzen so zusammen,
daß der Rechenaufwand minimal wird.
Ähnlichkeit mit Quadratwurzel-Algorithmus
Zwischen Quadrierungs- und Quadratwurzel-Algorithmus gibt es gewisse Analogien:
- Beim ersteren werden aus jeder Ziffer (außer der letzten) 2 Ziffernstellen,
beim letzteren werden aus je 2 Ziffern (außer ggf. der ersten) eine
- Beim ersteren werden mit 2 multiplizierte Summanden addiert, beim letzteren subtrahiert
Der Quadrierungs-Algorithmus hat auch eine Analogie mit der Bildung der n-ten Quadratzahl
als Summe der ersten n ungeraden Zahlen ( 12 = 1 , 22 = 1 + 3 , 32 = 1 + 3 + 5 , ... ), denn:
- Beide Methoden summieren größenmäßig gestaffelte, einander enthaltende Zahlen
Linker-Rest-Version
Es gibt obigen Quadrierungs-Algorithmus auch in einer ähnlichen Version,
die statt den rechten den linken Reststring jeder Ziffer summiert.
Man kann nämlich (s.o. Abschnitt "Herleitung") die Summanden auch so zusammenfassen: (1. Zeile unverändert)
N2 = n32*b6 + n22*b4 + n12*b2 + n02*b0 Jede Ziffer samt Stellenwert quadriert
+ ( n3*b3 + n2*b2 + n1*b1) * 2n0
+ ( n3*b4 + n2*b3 ) * 2n1
+ ( n3*b5 ) * 2n2
Daraus ergibt sich folgendes Rechenschema:
a_b_c_d 1_1_1_1 Zeile 1: Zahl gespreizt abc 111 Zeile 2 ab 11 Zeile 3 a 1 Zeile 4 ------- -------- Summe 11100001 Zeile 5: Summe = Ergebnis - Zeile 1: Schreibe die zu quadrierende Zahl gepreizt hin. Statt Nullen fügt man Unterstriche ein (übersichtlicher) - Zeilen 2, 3, 4: Für i = 0 bis imax-1: Falls Ziffer ni ≠ 0 : Schreibe unter Ziffer ni+1 rechtsbündig den Ziffernstring nmax ... ni+1 - Zeile 5: Summiere alles = Ergebnis
Auch hier kann man die komprimierte Schreibweise anwenden sowie (entsprechend geändert)
die Vereinfachung bei Einserblocks. Bei Einserblocks ist der Aufwand genauso gering wie bei
der Rechter-Rest-Version, aber die Summanden müssen etwas weiter verschoben werden, so daß das
Rechenschema nicht ganz so elegant ist
Weitere Versionen
Es lassen sich weitere Versionen des binären Quadrierungsalgorithmus entwerfen:
Man kann die kurzen Teilstrings zuerst hinschreiben, die (Rechter- und Linker-Rest)
Algorithmen arbeiten dann in umgekehrter Richtung. Aber dann stehen die langen Teilstrings
nicht mehr direkt unter der gespreizten Zahl, deshalb macht man beim Addieren eher Fehler.
Möglich sind auch Versionen, die ohne die gespreizte Zahl auskommen und deren Ziffern
gleich in die Teilstrings integrieren. Doch auch diese Versionen sind unergonomischer.
Weitere Algorithmen
Nach obigem Muster (Zifferndarstellung ausmultiplizieren, geeignet zusammenfassen,
praktisches Verfahren und ergonomische Schreibweise finden) lassen sich
weitere Algorithmen entwerfen, z.B.:
- schnelle Kubierung (Berechnung von N3 )
Die Zahl N wird hier stärker gespreizt (je 2 Ziffenzwischenräume),
man hat ein Mehrfaches an Summanden, und die Faktoren 3 oder 6 statt 2
- schnelle Wurzelberechnung
- verschiedene Multiplikationsalgorithmen
Geschichte
Obiger Algorithmus wurde von mir entwickelt. Es ist aber kaum vorstellbar, daß nicht schon andere Leute ihn entwickelt haben. Möglicherweise rechnen unsere Computer so - man kann schlecht hineinschauen, besonders nicht in die Chips mit Mathe-Prozeduren.
Homepage Leonhard Heinzmann veröffentlicht: 25. 3. 2013 Stand: 5. 1. 2014