[知识点] 6.2 快速幂
总目录 > 6 数学 > 6.2 快速幂
前言
很早就知道的一个知识点了,应用场景很多,很实用。
子目录列表
1、概念
2、算法描述
3、具体思路
4、代码
5、应用
6.2 快速幂
1、概念
快速幂,学名为二进制取幂(Binary Exponentiation),是一个在 O(log n) 时间内计算 a ^ n 的算法,对于 n 很大的情况下能大幅提高程序效率。不仅如此,它还可以应用在任何具有结合律的运算中。
2、算法描述
计算 a 的 n 次方,暴力算法即直接使用 for 循环进行 n 次相乘,时间复杂度为 O(n),当 n 较大时,显然不再适用。而快速幂算法利用指数能够进行分割来降低复杂度,已知 a ^ (b + c) = (a ^ b) * (a ^ c),a ^ (2 * b) = (a ^ b) ^ 2,那么,我们是否可以将较大的 n 进行拆分来计算呢?举个例子:
3 ^ 13 = 3 ^ (1101)2 = (3 ^ 8) * (3 ^ 4) * (3 ^ 1)
根据幂数的结合律,我们将其拆分成若干个 2 的次方数,比如上述例子 13 = 8 + 4 + 1,又可以通过计算得到 3 ^ 1 = 3, 3 ^ 2 = (3 ^ 1) ^ 2 = 9, 3 ^ 4 = (3 ^ 2) ^ 2 = 81, 3 ^ 8 = (3 ^ 4) ^ 2 = 6561,将其相乘即可。
不难看出,快速幂是一种倍增思想的算法。原式被分解成 a 的 2 ^ k 次幂的序列,我们从 a ^ 1 开始通过倍增一一求出序列各数的值,其时间复杂度显然为 O(log n)。
3、具体思路
上一节提到的位运算(请参见:6.1 位运算与进位制),这里便是其很好的一个运用场景。在拆分幂数时,显然使用二进制要比十进制来得轻松。以上面的例子进行描述:
> 初始结果为 1,初始倍增数为 3 ^ 1,13 的二进制为 (1101)2
> 当前最低位为 1,对应 3 ^ 1,结果乘上 3 ^ 1 变成 3,倍增数倍增一次变成 3 ^ 2,(1101)2 右移一位变成 (110)2
> 当前最低位为 0,对应 0 * 3 ^ 2,结果不变,倍增数倍增一次变成 3 ^ 4,(110)2 右移一位变成 (11)2
> 当前最低位为 1,对应 3 ^ 4,结果乘上 3 ^ 4 变成 3 ^ 5,倍增数倍增一次变成 3 ^ 8,(11)2 右移一位变成 (1)2
> 当前最低位为 1,对应 3 ^ 8,结果乘上 3 ^ 8 变成 3 ^ 13,倍增数倍增一次变成 3 ^ 16,(1)2 右移一位变成 (0)2,结束,退出循环
4、代码
int pow(int a, int b) { int o = a, res = 1; while (b) { if (b & 1) (res *= o) %= MOD; (o *= o) %= MOD; b >>= 1; } return res; }
代码展示的是最为常见的应用 —— 模意义下直接求幂。
根据费马小定理(请参见:6.4.2 费马小定理与欧拉定理),如果 b 是一个质数,可以直接计算 a ^ (b mod (MOD - 1))。
5、应用
除了直接求幂,还可以用于计算斐波那契数,多次置换等等。此处暂略。