Binäre Exponentiation
Die binäre Exponentiation (auch Square-and-Multiply genannt)
ist eine effiziente Methode zur Berechnung von natürlichen
Potenzen,
also Ausdrücken der Form
mit einer natürlichen Zahl
.
Dieser Algorithmus wurde bereits um ca. 200 v. Chr. in Indien entdeckt und ist in einem Werk namens Chandah-sûtra niedergeschrieben.
Motivation
Um
zu berechnen, kann man entweder
ausrechnen (drei Multiplikationen) oder
,
(zwei Multiplikationen), also
.
Ebenso können auch andere ganzzahlige Potenzen durch „fortgesetztes Quadrieren und gelegentliches Multiplizieren“ effizient berechnet werden.
Dieses Einsparen von Multiplikationen funktioniert sowohl für reelle Zahlen als auch für reellwertige Matrizen, elliptische Kurven und beliebige andere Halbgruppen.
Algorithmus
- Umwandlung des Exponenten
in die zugehörige Binärdarstellung.
- Ersetzen jeder 0 durch Q und jeder 1 durch QM.
- Nun wird Q als Anweisung zum Quadrieren und M als Anweisung zum Multiplizieren aufgefasst.
- Somit bildet die resultierende Zeichenkette
von links nach rechts gelesen eine Vorschrift zur Berechnung von
. Man beginne mit 1, quadriere für jedes gelesene Q das bisherige Zwischenergebnis und multipliziere es für jedes gelesene M mit
.
Da die Binärdarstellung von
immer mit der Ziffer 1 beginnt – und so ebenfalls die Anweisung mit QM
beginnt –, ergibt sich für die erste Anweisung QM in jedem Fall das
Zwischenergebnis
.
Aus diesem Grund ergibt sich eine leicht vereinfachte Variante, bei der die
erste Anweisung QM durch
ersetzt wird.
Beispiel (Algorithmus)
Sei k = 23. Die Binärdarstellung von 23 lautet 10111. Daraus ergibt
sich nach den Ersetzungen QM Q QM QM QM. Nach dem Streichen des führenden
QM-Paares hat man Q QM QM QM. Daraus können wir nun ablesen, dass
der Rechenvorgang folgendermaßen auszusehen hat: „quadriere, quadriere,
multipliziere mit ,
quadriere, multipliziere mit
,
quadriere, multipliziere mit
“.
Formal sieht das Ganze so aus:
bzw. sukzessive geschrieben:
Man sieht am Beispiel, dass man sich mit Hilfe der binären Exponentiation
einige Rechenschritte sparen kann. Anstatt von 22 Multiplikationen werden nur
noch 7 benötigt, indem man viermal quadriert und dreimal mit
multipliziert.
Pseudocode (Algorithmus)
Der Algorithmus ist in zwei Varianten dargestellt. Variante 1 verwendet eine if-Bedingung, um an den entsprechenden Stellen zu multiplizieren. Variante 2 baut die if-Bedingung implizit in den arithmetischen Ausdruck ein.
Variante 1 | Variante 2 |
---|---|
// Berechnet x^k // b ... binäre Darstellung von k // res ... Resultat der Berechnung function bin_exp(x,b) res = 1 for i = n..0 res = res^2 if b_i == 1 res = res * x end-if end-for return res end-function |
// Berechnet x^k // b ... binäre Darstellung von k // res ... Resultat der Berechnung function bin_exp(x,b) res = 1 for i = n..0 res = res^2 * x^{b_i} end-for return res end-function |
Alternativer Algorithmus
Man kann das Verfahren für eine Berechnung von Hand auch so gestalten, dass man zunächst die Basis oft genug quadriert und anschließend die richtigen Zahlen miteinander multipliziert. Dann ähnelt es der Russischen Bauernmultiplikation, welche die Multiplikation zweier Zahlen auf das Halbieren, Verdoppeln und Addieren von Zahlen zurückführt.
Dazu schreibt man den Exponenten links und die Basis rechts. Der Exponent wird schrittweise halbiert (das Ergebnis wird abgerundet) und die Basis schrittweise quadriert. Man streicht die Zeilen mit geradem Exponenten. Das Produkt der nichtgestrichenen rechten Zahlen ist die gesuchte Potenz.
Beispiel (alternativer Algorithmus)
218 | |
18 | 2 |
9 | 4 |
4 | 16 |
2 | 256 |
1 | 65.536 |
Ergebnis | 262.144 (= 4 · 65.536) |
Pseudocode (alternativer Algorithmus)
Anders als bei dem vorherigen Ansatz werden hier die benötigten Potenzen von
direkt aufmultipliziert. Diese Variante bietet sich an, wenn der Exponent
nicht explizit in Binärdarstellung vorliegt. Zur Veranschaulichung kann die
Gleichheit
betrachtet werden.
Bestimmt werden soll ,
ohne
in Binärdarstellung vorliegen zu haben.
Binäre Exponentiation |
---|
// Berechnet x^k
// res … Resultat der Berechnung
function res = bin_exp(x,k)
res = 1
while k > 0
if k mod 2 == 1
res = res * x
end-if
x = x^2
k = k DIV 2 //Ganzzahlige Division (das Ergebnis wird abgerundet)
end-while
return res
end-function
|
Rekursiver Algorithmus mit Herleitung
Jede natürliche Zahl
lässt sich eindeutig in
zerlegen, wobei
.
Aufgrund der Potenzgesetze ergibt sich
Der letzte Ausdruck beinhaltet lediglich
- eine Potenzierung mit einem Exponenten
, der nur noch etwa halb so groß wie
ist, welche rekursiv mit demselben Algorithmus berechnet werden kann,
- eine Quadrierung,
- eine Multiplikation,
- eine Potenzierung mit 0 oder 1 als Exponent.
Eine direkte Implementation in Haskell sähe wie folgt aus:
a^0 = 1
a^1 = a
a^2 = a*a
a^n = (a^m)^2 * a^b where (m, b) = n `divMod` 2
Die Funktion ^
, die hier definiert wird, stützt sich also auf
ein vorhandenes *
für die Multiplikation, divMod
für die Abspaltung der untersten Binärstelle des Exponenten und, rekursiv, sich
selbst.
Geringfügige Optimierungen, wie etwa die Umwandlung in eine endrekursive Variante, führen im Wesentlichen zu oben genannten iterativen Algorithmen.
Binäre Modulo-Exponentiation
Beim Rechnen modulo einer natürlichen Zahl ist eine leichte Modifikation anwendbar, die verhindert, dass die berechneten Zahlen zu groß werden: Man bildet nach jedem Quadrieren und Multiplizieren den Rest. Die zuvor vorgestellten Algorithmen können leicht durch diese Moduloperationen erweitert werden. Dieses Verfahren wird beispielsweise bei RSA-Verschlüsselung angewendet.
Beispiel
218 mod 39 | |
18 | 2 |
9 | 4 |
4 | 16 |
2 | 22 (= 256 mod 39) |
1 | 16 (= 484 mod 39) |
Ergebnis | 25 (= 4 · 16 mod 39 = 218 mod 39) |
Laufzeitanalyse
Bei der einfachen und langsamen Potenzierung
von
werden
Multiplikationen benötigt. Bei der binären Exponentiation wird die Schleife
lediglich
-mal
durchlaufen (
entspricht hierbei ungefähr der Länge der Zahl
in der Binärdarstellung). In jedem Schleifendurchlauf kommt es zu einer
Quadrierung (wobei die erste Quadrierung vernachlässigt werden kann) und
eventuell einer Multiplikation. Asymptotisch werden
Operationen (eventuell Langzahloperationen) benötigt, wogegen
Operationen bei der einfachen Potenzierung zu Buche schlagen.
bezeichnet eine asymptotische
obere Schranke für das Laufzeitverhalten des Algorithmus. Wie man leicht
einsieht, ist die binäre Exponentiation sehr viel effizienter als
das einfache Verfahren. Dieser verringerte Anspruch an die Rechenleistung ist bei
großen Basen und Exponenten enorm.
Ähnliche Algorithmen
Die binäre Exponentiation muss nicht notwendig die multiplikationssparendste
Berechnungsart einer Potenz sein. Um beispielsweise
zu berechnen, kann man entweder gemäß binärer Exponentiation
berechnen (6 Multiplikationen), oder aber
mit
(insgesamt 5 Multiplikationen).
Implementierung in C++
Es wird
verwendet, wie in der Funktion
pow
aus der STL. Statt
unsigned
kann jeder beschränkte vorzeichenlose ganzzahlige
Datentyp verwendet werden, falls nötig. Wird ein eigener Typ für Langzahlarithmetik
(LZA) benutzt, so lässt sich h
nicht wie hier bestimmen. Wichtig
ist die Stelle (#)
, d.h. eine Bitmaske, die das höchstwertige
Bit maskiert. Ob und wie dies effektiv möglich ist, hängt speziell vom LZA-Typ
ab. Immer möglich ist das Kopieren des Wertes und das anschließende Löschen
gesetzter Bits von hinten, bis die Kopie Null ist; oder ein anderes lineares
Verfahren.
// b: basis
// e: Exponent
// Annahmen: Typ T besitzt T operator*(T, T) und eine 1.
template<typename T>
T bin_exp(T b, unsigned e)
{
if (!e) return T(1);
// Ab hier: e != 0, d, h. irgendein Bit in e ist 1.
unsigned h = ~(~0u >> 1); // h = binär 100...0
while (!(e & h)) h >>= 1;
// h maskiert nun das erste gesetzte Bit in e. (#)
T r = b;
// solange weitere Bits zu prüfen sind (das erste wurde durch r = b bereits abgearbeitet),
while (h >>= 1)
{
r *= r; // quadrieren
if (e & h) r *= b; // falls Bit gesetzt, multiplizieren
}
// h == 0, d. h. alle Bits geprüft.
return r;
}



© biancahoegel.de
Datum der letzten Änderung: Jena, den: 06.01. 2024