快速幂
定义
快速幂,二进制取幂(Binary Exponentiation,也称平方法),是一个在 的时间内计算 的小技巧,而暴力的计算需要 的时间。
这个技巧也常常用在非计算的场景,因为它可以应用在任何具有结合律的运算中。其中显然的是它可以应用于模意义下取幂、矩阵幂等运算,我们接下来会讨论。
解释
计算 的 次方表示将 个 乘在一起:。然而当 太大的时侯,这种方法就不太适用了。不过我们知道:。二进制取幂的想法是,我们将取幂的任务按照指数的 二进制表示 来分割成更小的任务。

过程
迭代版本
首先我们将 表示为 2 进制,举一个例子:
因为 有 个二进制位,因此当我们知道了 后,我们只用计算 次乘法就可以计算出 。
于是我们只需要知道一个快速的方法来计算上述 3 的 次幂的序列。这个问题很简单,因为序列中(除第一个)任意一个元素就是其前一个元素的平方。举一个例子:
因此为了计算 ,我们只需要将对应二进制位为 1 的整系数幂乘起来就行了:
将上述过程说得形式化一些,如果把 写作二进制为 ,那么有:
其中 。那么就有
根据上式我们发现,原问题被我们转化成了形式相同的子问题的乘积,并且我们可以在常数时间内从 项推出 项。
这个算法的复杂度是 的,我们计算了 个 次幂的数,然后花费 的时间选择二进制为 1 对应的幂来相乘。
递归版本
上述迭代版本中,由于 项依赖于 ,使得其转换为递归版本比较困难(一方面需要返回一个额外的 ,对函数来说无法实现一个只返回计算结果的接口;另一方面则是必须从低位往高位计算,即从高位往低位调用,这也造成了递归实现的困扰),下面则提供递归版本的思路。
给定形式 ,即 表示将 的前 位二进制位当作一个二进制数,则有如下变换:
那么有:
如上所述,在递归时,对于不同的递归深度是相同的处理:,即将当前递归的二进制数拆成两部分:最低位在递归出来时乘上去,其余部分则变成新的二进制数递归进入更深一层作相同的处理。
可以观察到,每递归深入一层则二进制位减少一位,所以该算法的时间复杂度也为 。
实现
首先我们可以直接按照上述递归方法实现:
示例代码
第二种实现方法是非递归式的。它在循环的过程中将二进制位为 1 时对应的幂累乘到答案中。尽管两者的理论复杂度是相同的,但第二种在实践过程中的速度是比第一种更快的,因为递归会花费一定的开销。
示例代码