平方求幂

title: 基本方法.
content: 该方法是基于观察到,对于正整数formula_1,我们有  该方法使用指数的位(二进制的位,即bit,下文称为“位”)来确定计算哪些幂。  此例显示如何使用此方法计算 formula_3。 幂指数13的二进制为1101。这些位按照从左到右的顺序使用。 指数有4位,所以有4次迭代。  首先,将结果初始化为1:formula_4  这可以按照下面的递归算法来实现: 尽管不是尾调用,但是通过引入辅助函数,该算法可以被重写成尾递归算法:  该算法的迭代版本的辅助空间是有界的,代码如下
title: 计算复杂度.
content: 简要分析表明此算法用了 formula_12 次平方,以及至多 formula_12 次乘法,其中 formula_14 表示向下取整函数。更确切地说,做乘法的次数比 "n" 的二进制展开的次数要少一次。对于 "n" 大于4左右的时候,这种算法在计算上就已经比"天真地"将它与自身重复地相乘更高效了。  每次平方的结果大约是前一次结果的两倍,因此,如果两个 "d" 位数的相乘的实现要进行 O("d") 次计算(其中 "k" 为一固定值),那么计算 "x" 的复杂度为:
title: 2法.
content: 此算法先把指数展开成 2 形式,然后再计算 x 的值。它在1939年由Brauer首次提出。在下面的算法中,我们使用以下函数 f(0) = (k,0) 和 f(m) = (s,u),其中 m = u·2,"u" 为奇数。  算法:  为了获得最佳效率,"k" 应该是满足  的最小整数。
title: 滑动窗口法.
content: 此方法是 2 法的更高效的变体。例如,要计算398次幂,二进制展开为 (110 001 110),我们采用长度为3的窗,使用 2 法,需要计算 1, x, x, x, x, x, x, x, x, x, x, x。 但我们也可以计算 1, x, x, x, x, x, x, x, x, x, x,这就会省下一次乘法,相当于是计算 (110 001 110)n 的值  以下是一般算法:  算法:  算法:
title: 蒙哥马利阶梯法.
content: 求幂的许多算法都不提供对旁路攻击的防护。也就是说,监测到乘方运算的攻击者可以(部分)还原所计算的指数。就如众多公钥加密系统中那样,如果指数需要保密的话,这就是个问题了。一个叫做蒙哥马利阶梯的方法解决了这个问题。  给定一个非零正整数的二进制展开 n=(n...n)(其中 n=1),我们可以以下面方式计算 x:  该算法会执行一系列固定的操作(复杂度log n):无论每一位的具体值如何,指数中的每一位都会进行乘法和平方。  蒙哥马利阶梯法的这种具体实现还无法抵御缓存时序攻击:当根据秘密指数的位值访问不同的变量时,内存访问延迟仍可能被攻击者观察到。
title: 固定基底的幂.
content: 当基底固定而指数变化时,可以使用几种方法来计算 x。可以看出,预计算在这些算法中起着关键作用。
title: 姚期智的方法.
content: 姚期智的方法与 法不同,是把指数以 为基底展开,并按上面的算法进行计算。令,,, 为整数。  把指数 写成  令 。该算法使用等式  给定 的元素,指数 写成上述形式,并且预先计算 的值,元素 就可以用下面的算法计算了。  如果我们令,,那么这些 就是 以 为基的每一位。姚期智的方法是把之前的那些 收集到 u 中,which appear to the highest power ;in the next round those with power are collected in as well etc. 变量 y 被原始的 乘了 次,内第二高的指数乘了 次...... 该算法计算 要用 次乘法,存储 个元素。
title: 欧几里得法.
content: 欧几里德法首先在P.D Rooij的《使用预计算和向量加法链的高效求幂》(英语:Efficient exponentiation using precomputation and vector addition chains)中介绍。  这种计算群 中 formula_23( 为自然数)的方法是,递归地使用下面的等式:  给定群 中的基底元素,把指数 formula_1 用姚期智的方法写出来,然后就可以用预计算的 formula_27 个值 formula_28 计算 formula_23 了。  该算法首先在 中找到最大值,在找到集合 中的最大值。 然后递归求 的 次幂,把这个值乘以,赋值给 ;把 模 的结果赋值给 。
title: 更多应用.
content: 这一思路还可用于计算大指数幂除以一个数的余数。特别是在密码学中,在整数除以"q"的余数的环中计算幂是很有用处的。在群中计算整数幂也是很有用的,使用法则  该方法对所有半群都适用,通常用于计算矩阵的幂。  例如,如果使用朴素的方法,计算  将花费很长时间以及大量的存储空间:计算 13789,并除以2345求余。即便使用更有效的方法也需要很长时间:求13789的平方,除以2345求余,将结果乘以13789,再求余,一直往下计算。这会需要 formula_30 次模乘。  把“*”理解为 "x"*"y" = "xy" mod 2345(即相乘后取模)后,应用上面的平方求幂算法,只需要27次乘法和整数除法(所有这些都可以存储在单个机器字中)就可以完成计算了。
title: 示例实现.
title: 通过2的幂进行计算.
content: 这是用Ruby写的上述算法的非递归实现。  由于低级语言会将 n=n/2 隐式向0取整,n=n-1 对那些语言来说,就是冗余的步骤了。 n[0]是n的二进制表示的最右边的位,所以如果它是1,则该数是奇数,如果它是零,则该数是偶数。它也是以2为模n的余数。
title: 运行实例:计算 3.
content: parameter x = 3
title: 运行实例:计算 3.
content: result := 3  JavaScript-Demonstration: http://home.mnet-online.de/wzwz.de/temp/ebs/en.htm
title: 幂的乘积的计算.
content: 平方求幂也可用于计算2个或多个幂的乘积。 如果基础群或半群是可交换的,那么常常可以通过同时计算乘积来减少乘法的次数。
title: 例子.
content: 式子 a×b 可以分三步计算: 所以总共需要八次乘法。  更快的解法是同时计算这两个幂 总共只需要6次乘法。注意 a×b 计算了两次;结果可以在第一次计算后存储,这将乘法计数减少到5次。  有数字的例子:  如果至少有两个指数大于1的话,同时计算幂就会比单独计算减少乘法次数。
title: 使用变换.
content: 如果表达式在计算前进行变换,上面的例子 a×b 也可以只用5次乘法就计算出来:  a×b = a×(ab) 其中 ab := a×b 这个变换可以推广成下面的方案:<br> 对于计算 a×b×...×m×n<br> 首先:定义 ab := a×b, abc = ab×c,...<br> 然后:计算变换后的表达式 a×ab×...×abc..m×abc..mn  在计算之前进行变换通常会减少乘法计数,但在某些情况下也会增加计数(请参见下面最后一个示例),因此在使用变换后的表达式进行计算之前,最好检查一下乘法的次数。
title: 例子.
content: 对于下面的表达式,表中显示了分开计算每个幂,在不进行变换的情况下同时进行计算,以及在变换后同时进行计算的乘法次数。
title: 用有符号数字重新编码.
content: 在某些计算中,如果允许负系数(也就会需要用基底的倒数)的话,只要在 中计算倒数很快或者已经预先计算,求幂会更加高效。例如,当计算 时,二进制方法需要 次乘法和 次平方。不过可以用 次平方得到,然后乘以 得到 。  为此,我们定义以 为基数的整数 的有符号数字表示为  Koyama和Tsuruoka的另一种算法并不要求 formula_42 这样的条件;它仍然可以让汉明重量最小化。
title: 替代方法及推广.
content: 平方求幂可以看作是一个次优的加法链求幂算法:它通过由重复指数加倍(平方)和指数递增(乘以 "x")组成的加法链来计算指数。更一般地,如果允许"任何"先前计算的指数相加(通过乘以 "x" 的幂),有时可以让求幂运算的乘法次数更少(但通常使用更多的内存)。"n"=15 时的最少次数:  一般来说,求给定指数的最佳加法链是一个难题,因为没有已知的高效算法,所以最优链通常只用于小指数(比如,在编译器中已经预先存储了小指数幂的最佳链)。不过,有一些启发式算法,虽然不是最优的,但是由于额外的簿记工作和内存使用量的增加而导致的乘法次数少于平方求幂。无论如何,乘法的次数永远不会比 Θ(log "n") 增长得更慢,所以这些算法只能减小平方求幂的渐进复杂度的常数因子。

Summary: 在数学和程序设计中,平方求幂(英语:exponentiating by squaring)或快速幂是快速计算一个数(或更一般地说,一个半群的元素,如多项式或方阵)的大正整数幂的一般方法。这些算法可以非常通用,例如用在模算数或矩阵幂。对于通常使用加性表示法的半群,如密码学中使用的椭圆曲线,这种方法也称为double-and-add。