Exponentiation by squaring: Difference between revisions

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search
imported>Bubba73
imported>Oneplusnine
mNo edit summary
 
Line 8: Line 8:


===Recursive version===
===Recursive version===
The method is based on the observation that, for any integer <math>n > 0</math>, one has:
The method is based on the observation that, for any integer <math>n > 0</math>, one has
<math display="block"> x^n=
<math display="block"> x^n=
    \begin{cases}
\begin{cases}
                x \, ( x^{2})^{(n - 1)/2}, & \mbox{if } n \mbox{ is odd} \\
  x \, (x^2)^{(n-1)/2} & \text{if } n \text{ is odd}, \\
                (x^{2})^{n/2} , & \mbox{if } n \mbox{ is even}
  (x^2)^{n/2} & \text{if } n \text{ is even}.
    \end{cases}
\end{cases}
</math>
</math>


If the exponent {{mvar|n}} is zero then the answer is 1. If the exponent is negative then we can reuse the previous formula by rewriting the value using a positive exponent. That is,
If the exponent {{mvar|n}} is zero, then the answer is 1. If the exponent is negative then we can reuse the previous formula by rewriting the value using a positive exponent. That is,
<math display="block">x^n = \left(\frac{1}{x}\right)^{-n}\,.</math>
<math display="block">
x^n = \left(\frac{1}{x}\right)^{-n}.
</math>


Together, these may be implemented directly as the following [[recursion (computer science)|recursive algorithm]]:
Together, these may be implemented directly as the following [[recursion (computer science)|recursive algorithm]]:


  '''Inputs''': a real number ''x''; an integer ''n''
  '''Inputs''': a real number ''x''; an integer ''n''
  '''Output''': x<sup>n</sup>
  '''Output''': ''x<sup>n</sup>''
   
   
  '''function''' exp_by_squaring(x, n) '''is'''
  '''function''' exp_by_squaring(''x'', ''n'') '''is'''
     '''if''' ''n'' < 0 '''then'''
     '''if''' ''n'' < 0 '''then'''
         '''return''' exp_by_squaring(1 / ''x'', −''n'')
         '''return''' exp_by_squaring(1 / ''x'', −''n'')
Line 35: Line 37:
  '''end function'''
  '''end function'''


In each recursive call, the least-significant digit of the [[binary representation]] of {{mvar|n}} is removed. It follows that the number of recursive calls is <math>\lceil \log_2 n\rceil,</math> the number of [[bit]]s of the binary representation of {{mvar|n}}. So this algorithm computes this number of squares and a lower number of multiplication, which is equal to the number of ''1''s in the binary representation of {{mvar|n}}. This logarithmic number of operations is to be compared with the trivial algorithm which requires {{math|''n'' − 1}} multiplications.
In each recursive call, the least-significant digit of the [[binary representation]] of {{mvar|n}} is removed. It follows that the number of recursive calls is <math>\lceil \log_2 n\rceil,</math> the number of [[bit]]s of the binary representation of {{mvar|n}}. So this algorithm computes this number of squares and a lower number of multiplication, which is equal to the number of 1s in the binary representation of {{mvar|n}}. This logarithmic number of operations is to be compared with the trivial algorithm which requires {{math|''n'' − 1}} multiplications.


This algorithm is not [[Tail call|tail-recursive]]. This implies that it requires an amount of auxiliary memory that is roughly proportional to the number of recursive calls -- or perhaps higher if the amount of data per iteration is increasing.
This algorithm is not [[Tail call|tail-recursive]]. This implies that it requires an amount of auxiliary memory that is roughly proportional to the number of recursive calls, or perhaps higher if the amount of data per iteration is increasing.


The algorithms of the next section use a different approach, and the resulting algorithms needs the same number of operations, but use an auxiliary memory that is roughly the same as the memory required to store the result.
The algorithms of the next section use a different approach, and the resulting algorithms needs the same number of operations, but use an auxiliary memory that is roughly the same as the memory required to store the result.
Line 44: Line 46:


The variants described in this section are based on the formula
The variants described in this section are based on the formula
:<math> yx^n=
<math display="block">
    \begin{cases}
y x^n = \begin{cases}
                (yx) \, ( x^{2})^{(n - 1)/2}, & \mbox{if } n \mbox{ is odd} \\
  yx\,(x^2)^{(n-1)/2} & \text{if } n \text{ is odd}, \\
                y\,(x^{2})^{n/2} , & \mbox{if } n \mbox{ is even}.
  y\,(x^2)^{n/2} & \text{if } n \text{ is even}.
    \end{cases}
\end{cases}
</math>
</math>


Line 83: Line 85:
</syntaxhighlight>
</syntaxhighlight>


The correctness of the algorithm results from the fact that <math>yx^n</math> is invariant during the computation; it is <math>1\cdot x^n=x^n</math> at the beginning; and it is <math>yx^1=xy </math> at the end.
The correctness of the algorithm results from the fact that <math>yx^n</math> is invariant during the computation; it is <math>1 \cdot x^n = x^n</math> at the beginning; and it is <math>yx^1 = xy</math> at the end.


These algorithms use exactly the same number of operations as the algorithm of the preceding section, but the multiplications are done in a different order.
These algorithms use exactly the same number of operations as the algorithm of the preceding section, but the multiplications are done in a different order.
Line 98: Line 100:


==2<sup>''k''</sup>-ary method==
==2<sup>''k''</sup>-ary method==
This algorithm calculates the value of ''x<sup>n</sup>'' after expanding the exponent in base 2<sup>''k''</sup>. It was first proposed by [[Brauer]] in 1939. In the algorithm below we make use of the following function ''f''(0) = (''k'', 0) and ''f''(''m'') = (''s'', ''u''), where ''m'' = ''u''·2<sup>''s''</sup> with ''u'' odd.
This algorithm calculates the value of ''x<sup>n</sup>'' after expanding the exponent in base 2<sup>''k''</sup>. It was first proposed by [[Alfred_Brauer|Brauer]] in 1939. In the algorithm below we make use of the following function ''f''(0) = (''k'', 0) and ''f''(''m'') = (''s'', ''u''), where ''m'' = ''u''·2<sup>''s''</sup> with ''u'' odd.


Algorithm:
Algorithm:
Line 215: Line 217:
  {{nowrap|'''Return''' <math>x^n = x_M^{n_M}</math>.}}
  {{nowrap|'''Return''' <math>x^n = x_M^{n_M}</math>.}}


The algorithm first finds the largest value among the {{math|''n''<sub>''i''</sub>}} and then the supremum within the set of {{math|{{(}} ''n''<sub>''i''</sub> \ ''i'' ≠ ''M'' {{)}}}}.
The algorithm first finds the largest value among the {{math|''n''<sub>''i''</sub>}} and then the [[Infimum and supremum|supremum]] within the set of {{math|{{(}} ''n''<sub>''i''</sub> \ ''i'' ≠ ''M'' {{)}}}}.
Then it raises {{math|''x''<sub>''M''</sub>}} to the power {{mvar|q}}, multiplies this value with {{math|''x''<sub>''N''</sub>}}, and then assigns {{math|''x''<sub>''N''</sub>}} the result of this computation and {{math|''n''<sub>''M''</sub>}} the value {{math|''n''<sub>''M''</sub>}} modulo {{math|''n''<sub>''N''</sub>}}.
Then it raises {{math|''x''<sub>''M''</sub>}} to the power {{mvar|q}}, multiplies this value with {{math|''x''<sub>''N''</sub>}}, and then assigns {{math|''x''<sub>''N''</sub>}} the result of this computation and {{math|''n''<sub>''M''</sub>}} the value {{math|''n''<sub>''M''</sub>}} modulo {{math|''n''<sub>''N''</sub>}}.


Line 231: Line 233:
The approach also works in [[non-commutative]] semigroups and is often used to compute powers of [[matrix (mathematics)|matrices]].
The approach also works in [[non-commutative]] semigroups and is often used to compute powers of [[matrix (mathematics)|matrices]].


More generally, the approach works with positive integer exponents in every [[magma (algebra)|magma]] for which the binary operation is [[power associative]].
More generally, the approach works with positive integer exponents in every [[magma (algebra)|magma]] for which the [[binary operation]] is [[power associative]].


==Signed-digit recoding==
==Signed-digit recoding==

Latest revision as of 17:46, 16 October 2025

Template:Short description Script error: No such module "Unsubst". Template:More citations needed

In mathematics and computer programming, exponentiating by squaring is a general method for fast computation of large positive integer powers of a number, or more generally of an element of a semigroup, like a polynomial or a square matrix. Some variants are commonly referred to as square-and-multiply algorithms or binary exponentiation. These can be of quite general use, for example in modular arithmetic or powering of matrices. For semigroups for which additive notation is commonly used, like elliptic curves used in cryptography, this method is also referred to as double-and-add.

Basic method

Recursive version

The method is based on the observation that, for any integer n>0, one has xn={x(x2)(n1)/2if n is odd,(x2)n/2if n is even.

If the exponent Template:Mvar is zero, then the answer is 1. If the exponent is negative then we can reuse the previous formula by rewriting the value using a positive exponent. That is, xn=(1x)n.

Together, these may be implemented directly as the following recursive algorithm:

Inputs: a real number x; an integer n
Output: xn

function exp_by_squaring(x, n) is
    if n < 0 then
        return exp_by_squaring(1 / x, −n)
    else if n = 0 then
        return 1
    else if n is even then
        return exp_by_squaring(x × x, n / 2)
    else if n is odd then
        return x × exp_by_squaring(x × x, (n − 1) / 2)
end function

In each recursive call, the least-significant digit of the binary representation of Template:Mvar is removed. It follows that the number of recursive calls is log2n, the number of bits of the binary representation of Template:Mvar. So this algorithm computes this number of squares and a lower number of multiplication, which is equal to the number of 1s in the binary representation of Template:Mvar. This logarithmic number of operations is to be compared with the trivial algorithm which requires Template:Math multiplications.

This algorithm is not tail-recursive. This implies that it requires an amount of auxiliary memory that is roughly proportional to the number of recursive calls, or perhaps higher if the amount of data per iteration is increasing.

The algorithms of the next section use a different approach, and the resulting algorithms needs the same number of operations, but use an auxiliary memory that is roughly the same as the memory required to store the result.

With constant auxiliary memory

The variants described in this section are based on the formula yxn={yx(x2)(n1)/2if n is odd,y(x2)n/2if n is even.

If one applies recursively this formula, by starting with Template:Math, one gets eventually an exponent equal to Template:Math, and the desired result is then the left factor.

This may be implemented as a tail-recursive function:

Function exp_by_squaring(x, n)
    return exp_by_squaring2(1, x, n)

Function exp_by_squaring2(y, x, n)
    if n < 0 then return exp_by_squaring2(y, 1 / x, -n);
    else if n = 0 then return y;
    else if n is even then return exp_by_squaring2(y, x * x, n / 2);
    else if n is odd then return exp_by_squaring2(x * y, x * x, (n - 1) / 2).

The iterative version of the algorithm also uses a bounded auxiliary space, and is given by

Function exp_by_squaring_iterative(x, n)
    if n < 0 then
        x := 1 / x;
        n := -n;
    if n = 0 then return 1
    y := 1;
    while n > 1 do
        if n is odd then
            y := x * y;
            n := n - 1;
        x := x * x;
        n := n / 2;
    return x * y

The correctness of the algorithm results from the fact that yxn is invariant during the computation; it is 1xn=xn at the beginning; and it is yx1=xy at the end.

These algorithms use exactly the same number of operations as the algorithm of the preceding section, but the multiplications are done in a different order.

Computational complexity

A brief analysis shows that such an algorithm uses log2n squarings and at most log2n multiplications, where denotes the floor function. More precisely, the number of multiplications is one less than the number of ones present in the binary expansion of n. For n greater than about 4 this is computationally more efficient than naively multiplying the base with itself repeatedly.

Each squaring results in approximately double the number of digits of the previous, and so, if multiplication of two d-digit numbers is implemented in O(dk) operations for some fixed k, then the complexity of computing xn is given by

i=0O(logn)(2iO(logx))k=O((nlogx)k).

2k-ary method

This algorithm calculates the value of xn after expanding the exponent in base 2k. It was first proposed by Brauer in 1939. In the algorithm below we make use of the following function f(0) = (k, 0) and f(m) = (s, u), where m = u·2s with u odd.

Algorithm:

Input
An element x of G, a parameter k > 0, a non-negative integer Template:Math and the precomputed values x3,x5,...,x2k1.
Output
The element xn in G
y := 1; i := l - 1
while i ≥ 0 do
    (s, u) := f(ni)
    for j := 1 to k - s do
        y := y2
    y := y * xu
    for j := 1 to s do
        y := y2
    i := i - 1
return y

For optimal efficiency, k should be the smallest integer satisfying[1]

lgn<k(k+1)22k2k+1k2+1.

Sliding-window method

This method is an efficient variant of the 2k-ary method. For example, to calculate the exponent 398, which has binary expansion (110 001 110)2, we take a window of length 3 using the 2k-ary method algorithm and calculate 1, x3, x6, x12, x24, x48, x49, x98, x99, x198, x199, x398. But, we can also compute 1, x3, x6, x12, x24, x48, x96, x192, x199, x398, which saves one multiplication and amounts to evaluating (110 001 110)2

Here is the general algorithm:

Algorithm:

Input
An element x of G, a non negative integer Template:Math, a parameter k > 0 and the pre-computed values x3,x5,...,x2k1.
Output
The element xnG.

Algorithm:

y := 1; i := l - 1
while i > -1 do
    if ni = 0 then
        y := y2
        i := i - 1
    else
        s := max{i - k + 1, 0}
        while ns = 0 do
            s := s + 1[notes 1]
        for h := 1 to i - s + 1 do
            y := y2
        u := (ni, ni-1, ..., ns)2
        y := y * xu
        i := s - 1
return y

Montgomery's ladder technique

Many algorithms for exponentiation do not provide defence against side-channel attacks. Namely, an attacker observing the sequence of squarings and multiplications can (partially) recover the exponent involved in the computation. This is a problem if the exponent should remain secret, as with many public-key cryptosystems. A technique called "Montgomery's ladder"[2] addresses this concern.

Given the binary expansion of a positive, non-zero integer n = (nk−1...n0)2 with nk−1 = 1, we can compute xn as follows:

x1 = x; x2 = x2
for i = k - 2 to 0 do
    if ni = 0 then
        x2 = x1 * x2; x1 = x12
    else
        x1 = x1 * x2; x2 = x22
return x1

The algorithm performs a fixed sequence of operations (up to log n): a multiplication and squaring takes place for each bit in the exponent, regardless of the bit's specific value. A similar algorithm for multiplication by doubling exists.

This specific implementation of Montgomery's ladder is not yet protected against cache timing attacks: memory access latencies might still be observable to an attacker, as different variables are accessed depending on the value of bits of the secret exponent. Modern cryptographic implementations use a "scatter" technique to make sure the processor always misses the faster cache.[3]

Fixed-base exponent

There are several methods which can be employed to calculate xn when the base is fixed and the exponent varies. As one can see, precomputations play a key role in these algorithms.

Yao's method

Yao's method is orthogonal to the Template:Math-ary method where the exponent is expanded in radix Template:Math and the computation is as performed in the algorithm above. Let Template:Mvar, Template:Mvar, Template:Mvar, and Template:Mvar be integers.

Let the exponent Template:Mvar be written as

n=i=0w1nibi,

where 0ni<h for all i[0,w1].

Let Template:Math.

Then the algorithm uses the equality

xn=i=0w1xini=j=1h1[ni=jxi]j.

Given the element Template:Mvar of Template:Mvar, and the exponent Template:Mvar written in the above form, along with the precomputed values Template:Math, the element Template:Mvar is calculated using the algorithm below:

y = 1, u = 1, j = h - 1
while j > 0 do
    for i = 0 to w - 1 do
        if ni = j then
            u = u × xbi
    y = y × u
    j = j - 1
return y

If we set Template:Math and Template:Math, then the Template:Mvar values are simply the digits of Template:Mvar in base Template:Mvar. Yao's method collects in u first those Template:Mvar that appear to the highest power Template:Tmath; in the next round those with power Template:Tmath are collected in Template:Mvar as well etc. The variable y is multiplied Template:Tmath times with the initial Template:Mvar, Template:Tmath times with the next highest powers, and so on. The algorithm uses Template:Tmath multiplications, and Template:Tmath elements must be stored to compute Template:Mvar.[1]

Euclidean method

The Euclidean method was first introduced in Efficient exponentiation using precomputation and vector addition chains by P.D Rooij.

This method for computing xn in group Template:Math, where Template:Mvar is a natural integer, whose algorithm is given below, is using the following equality recursively:

x0n0x1n1=(x0x1q)n0x1n1modn0,

where q=n1n0. In other words, a Euclidean division of the exponent Template:Math by Template:Math is used to return a quotient Template:Mvar and a rest Template:Math.

Given the base element Template:Mvar in group Template:Math, and the exponent n written as in Yao's method, the element xn is calculated using l precomputed values xb0,...,xbli and then the algorithm below.

Begin loop
    Find M[0,l1], such that i[0,l1],nMni.
    Find N([0,l1]M), such that i([0,l1]M),nNni.
    Break loop if nN=0.
    Let q=nM/nN, and then let nN=(nMmodnN).
    Compute recursively xMq, and then let xN=xNxMq.
End loop;
Return xn=xMnM.

The algorithm first finds the largest value among the Template:Math and then the supremum within the set of Template:Math. Then it raises Template:Math to the power Template:Mvar, multiplies this value with Template:Math, and then assigns Template:Math the result of this computation and Template:Math the value Template:Math modulo Template:Math.

Further applications

The approach also works with semigroups that are not of characteristic zero, for example allowing fast computation of large exponents modulo a number. Especially in cryptography, it is useful to compute powers in a ring of [[modular arithmetic|integers modulo Template:Mvar]]. For example, the evaluation of

Template:Math

would take a very long time and much storage space if the naïve method of computing Template:Math and then taking the remainder when divided by 2345 were used. 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.

Applying above exp-by-squaring algorithm, with "*" interpreted as Template:Math (that is, a multiplication followed by a division with remainder) leads to only 27 multiplications and divisions of integers, which may all be stored in a single machine word. Generally, any of these approaches will take fewer than Template:Math modular multiplications.

The approach can also be used to compute integer powers in a group, using either of the rules

Template:Math,
Template:Math.

The approach also works in non-commutative semigroups and is often used to compute powers of matrices.

More generally, the approach works with positive integer exponents in every magma for which the binary operation is power associative.

Signed-digit recoding

In certain computations it may be more efficient to allow negative coefficients and hence use the inverse of the base, provided inversion in Template:Mvar is "fast" or has been precomputed. For example, when computing Template:Math, the binary method requires Template:Math multiplications and Template:Math squarings. However, one could perform Template:Mvar squarings to get Template:Math and then multiply by Template:Math to obtain Template:Math.

To this end we define the signed-digit representation of an integer Template:Mvar in radix Template:Mvar as

n=i=0l1nibi with |ni|<b.

Signed binary representation corresponds to the particular choice Template:Math and ni{1,0,1}. It is denoted by (nl1n0)s. There are several methods for computing this representation. The representation is not unique. For example, take Template:Math: two distinct signed-binary representations are given by (101¯11001¯10)s and (1001¯10001¯0)s, where 1¯ is used to denote Template:Math. Since the binary method computes a multiplication for every non-zero entry in the base-2 representation of Template:Mvar, we are interested in finding the signed-binary representation with the smallest number of non-zero entries, that is, the one with minimal Hamming weight. One method of doing this is to compute the representation in non-adjacent form, or NAF for short, which is one that satisfies nini+1=0 for all i0 and denoted by (nl1n0)NAF. For example, the NAF representation of 478 is (10001¯0001¯0)NAF. This representation always has minimal Hamming weight. A simple algorithm to compute the NAF representation of a given integer n=(nlnl1n0)2 with nl=nl1=0 is the following:

c0=0
for Template:Math to Template:Math do
  ci+1=12(ci+ni+ni+1)
  ni=ci+ni2ci+1
return (nl1n0)NAF

Another algorithm by Koyama and Tsuruoka does not require the condition that ni=ni+1=0; it still minimizes the Hamming weight.

Alternatives and generalizations

Script error: No such module "Labelled list hatnote". Exponentiation by squaring can be viewed as a suboptimal addition-chain exponentiation algorithm: it computes the exponent by an addition chain consisting of repeated exponent doublings (squarings) and/or incrementing exponents by one (multiplying by x) only. More generally, if one allows any previously computed exponents to be summed (by multiplying those powers of x), one can sometimes perform the exponentiation using fewer multiplications (but typically using more memory). The smallest power where this occurs is for n = 15:

x15=x×(x×[x×x2]2)2 (squaring, 6 multiplies),
x15=x3×([x3]2)2 (optimal addition chain, 5 multiplies if x3 is re-used).

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 used for small exponents only (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 improve asymptotically upon exponentiation by squaring by only a constant factor at best.

See also

Notes

Template:Reflist

References

Template:Reflist

  1. a b Script error: No such module "citation/CS1".
  2. Script error: No such module "Citation/CS1".
  3. Script error: No such module "Citation/CS1".


Cite error: <ref> tags exist for a group named "notes", but no corresponding <references group="notes"/> tag was found