[知识点] 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、应用

除了直接求幂,还可以用于计算斐波那契数,多次置换等等。此处暂略。

posted @ 2020-07-17 22:36  jinkun113  阅读(368)  评论(0编辑  收藏  举报