The following recursive algorithm computes xn for a non-negative integer n:
Compared to the ordinary method of multiplying x with itself n − 1 times, in this algorithm the "n is even" case is optimized, according to:
xn can be calculated thus, if n is an integer:
(This method works out 00 as 1.)
The same idea allows fast computation of large exponents modulo a number. Especially in cryptography, it is useful to compute powers in a ring of integers modulo q. It can also be used to compute integer powers in a group, using the rule
The method works in every semigroup and is often used to compute powers of matrices,
For example, the evaluation of
would take a very long time and lots of storage space if the naïve method is used: compute 13789722341 then take the remainder when divided by 2345. Even using a more effective method will take a long time: square 13789, take the remainder when divided by 2345, multiply the result by 13789, and so on. This will take 722340 modular multiplications. The square-and-multiply algorithm is based on the observation that 13789722341 = 13789(137892)361170. So, if we computed 137892, then the full computation would only take 361170 modular multiplications. This is a gain of a factor of two. But since the new problem is of the same type, we can apply the same observation again, once more approximately halving the size.
The repeated application of this algorithm is equivalent to decomposing the exponent (by a base conversion to binary) into a sequence of squares and products: for example
Some more examples:
In most statically typed languages, result=1 must be replaced with code assigning an identity matrix of the same size as x to result to get a matrix exponentiating algorithm. In Ruby, thanks to coercion, result is automatically upgraded to the appropriate type, so this function works with matrices as well as with integers and floats. Note that n=n-1 is redundant when n=n/2 implicitly rounds towards zero, as lower level languages would do.
result = 1
while n.nonzero?
if n[0].nonzero?
result = result * x
n = n-1
end
x = x*x
n = n/2
end
return result
end
The following is the C programming language equivalent program.
long result = 1;
while (n ) {
if (n & 1 ) {
result *= x;
}
x *= x;
n /= 2;
}
return result;
}
parameter x = 3
parameter n = 10
result := 1
Iteration 1
n = 10 -> n is even
x := x2 = 32 = 9
n := n / 2 = 5
Iteration 2
n = 5 -> n is odd
-> result := result * x = 1 * x = 1 * 32 = 9
n := n - 1 = 4
x := x2 = 92 = 34 = 81
n := n / 2 = 2
Iteration 3
n = 2 -> n is even
x := x2 = 812 = 38 = 6561
n := n / 2 = 1
Iteration 4
n = 1 -> n is odd
-> result := result * x = 32 * 38 = 310 = 9 * 6561 = 59049
n := n - 1 = 0
return result
result := 3
bin := "1010"
Iteration for digit 2:
result := result2 = 32 = 9
1010bin - Digit equals "0"
Iteration for digit 3:
result := result2 = (32)2 = 34 = 81
1010bin - Digit equals "1" --> result := result*3 = (32)2*3 = 35 = 243
Iteration for digit 4:
result := result2 = ((32)2*3)2 = 310 = 59049
1010bin - Digit equals "0"
return result
JavaScript-Demonstration: http://home.arcor.de/wzwz.de/wiki/ebs/en.htm
We may call * a "multiplication" and define an "exponentiation" E in the following way:
For all elements a of S:
Now the algorithm exponentiation by squaring may be used for fast computing of E-values.
Example using javascript:
if (s == "" || n < 1 ) return ""
var res = s
var bin = n.toString (2 )
for (var i = 1 ; i < bin.length ; i++ ) {
res = res + res
if (bin.charAt (i ) == '1' ) res = res + s
}
return res
}
The call repeat ('Abc', 6 ) returns the string AbcAbcAbcAbcAbcAbc
A faster solution is to calculate both powers simultaneously:
Example with numbers:
Calculating the powers simultaneously instead of calculating them separately always reduces the count of multiplications if at least two of the exponents are greater than 1.
a7×b5 = a2×(ab)5 with ab := a×b
Generalization of transformation shows the following scheme:
For calculating aA×bB×...×mM×nN
1st: define ab := a×b, abc = ab×c, ...
2nd: calculate the transformed expression aA-B×abB-C×...×abc..mM-N×abc..mnN
Transformation before calculation often reduces the count of multiplications but in some cases it also increases the count (see the last one of the examples below), so it may be a good idea to check the count of multiplications before using the transformed expression for calculation.
Example: a7×b5×c3
separate: [((a)2×a)2×a] × [((b)2)2×b] × [(c)2×c] (11 multiplications )
simultaneous: ((a×b)2×a×c)2×a×b×c (8 multiplications )
transformation: a := 2 ab := a×b abc := ab×c (2 multiplications )
calculation after that: (a×ab×abc)2×abc (4 multiplications ⇒ 6 in total )
Example: a5×b5×c3
separate: [((a)2)2×a] × [((b)2)2×b] × [(c)2×c] (10 multiplications )
simultaneous: ((a×b)2×c)2×a×b×c (7 multiplications )
transformation: a := 2 ab := a×b abc := ab×c (2 multiplications )
calculation after that: (ab×abc)2×abc (3 multiplications ⇒ 5 in total )
Example: a7×b4×c1
separate: [((a)2×a)2×a] × [((b)2)2] × [c] (8 multiplications )
simultaneous: ((a×b)2×a)2×a×c (6 multiplications )
transformation: a := 2 ab := a×b abc := ab×c (2 multiplications )
calculation after that: (a×ab)2×a×ab×abc (5 multiplications ⇒ 7 in total )
// the following javascript function calculates
// Bas [0] ^ Exp [0] x Bas [1] ^ Exp [1] x ...
function productOfPowers_simpleVersion (Bas , Exp ) {
var str // temporary string
// make binary representations:
var maxLen = 0
var bin = new Array ()
for (var i = 0 ; i < Exp.length ; i++ ) {
str = Exp [i] . toString (2 )
bin [i] = str
if (maxLen < str.length ) maxLen = str.length
}
// make all binaries the same length:
for (var i = 0 ; i < bin.length ; i++ ) {
while (bin [i] . length < maxLen ) bin [i] = '0' + bin [i]
}
// calculate:
var result = 1
// . use first binary digits:
for (var y = 0 ; y < bin.length ; y++ ) {
str = bin [y]
if (str.charAt (0 ) == '1' ) {
if (result == 1 ) result = Bas [y] ; else result = result * Bas [y]
}
}
// . use remaining digits:
for (var x = 1 ; x < maxLen ; x++ ) { // x : all digits except first one
result = result * result
for (var y = 0 ; y < bin.length ; y++ ) { // y : all factors
str = bin [y]
if (str.charAt (x ) == '1' ) result = result * Bas [y]
}
}
// ready:
return result
}
//
// for the following function input has to be sorted:
// Exp [0] >= Exp [1] >= ...
function productOfPowers_withTransformation (Bas , Exp ) {
// new bases:
var tempBas = new Array ()
tempBas [0] = Bas [0]
for (var i = 1 ; i < Bas.length ; i++ ) tempBas [i] = Bas [i] * tempBas [i-1]
// new exponents:
var tempExp = new Array ()
for (var i = 0 ; i < Exp.length - 1 ; i++ ) tempExp [i] = Exp [i] - Exp [i+1]
tempExp [Exp.length-1] = Exp [Exp.length-1]
// now compress:
var basTrans = new Array ()
var expTrans = new Array ()
for (var i = 0 ; i < tempExp.length ; i++ ) if (tempExp [i] > 0 ) {
basTrans.push (tempBas [i] )
expTrans.push (tempExp [i] )
}
// ready:
return productOfPowers_simpleVersion (basTrans , expTrans )
}
// now let's test it:
alert ('S1: ' + productOfPowers_simpleVersion ([2 , 3 ] , [7 , 5 ] ) ) // should be 31,104
alert ('T1: ' + productOfPowers_withTransformation ([2 , 3 ] , [7 , 5 ] ) ) // once again: 31,104
alert ('T2: ' + productOfPowers_withTransformation ([2 , 5 , 3 ] , [4 , 3 , 2 ] ) ) // 18,000
alert ('T3: ' + productOfPowers_withTransformation ([2 , 5 , 3 ] , [3 , 3 , 2 ] ) ) // 9,000
alert ('T4: ' + productOfPowers_withTransformation ([2 , 5 , 3 ] , [4 , 3 , 3 ] ) ) // 54,000
alert ('T5: ' + productOfPowers_withTransformation ([2 , 5 , 3 ] , [3 , 3 , 3 ] ) ) // 27,000
It is very similar in properties to aforementioned approach. While recursion is a natural way for calculation, it can be easily translated to iterative form. It also might provide some computational advantage (e.g., in case of small x and n = 3*2m) as well as memory consumption reduction.
In general, finding the optimal addition chain for a given exponent is a hard problem, for which no efficient algorithms are known, so optimal chains are typically only used for small exponents (e.g. in compilers where the chains for small powers have been pre-tabulated). However, there are a number of heuristic algorithms that, while not being optimal, have fewer multiplications than exponentiation by squaring at the cost of additional bookkeeping work and memory usage. Regardless, the number of multiplications never grows more slowly than Θ(log n), so these algorithms only improve asymptotically upon exponentiation by squaring by a constant factor at best.