Skip to content

快速幂

2021-07-31

快速幂 ​

如何快速求 ab(a,00b∈Z)?

递归写法 ​

根据乘方公式 am+n=00am⋅an,有:

ab={a⌊b/2⌋⋅a⌊b/2⌋b=2ka⌊b/2⌋⋅a⌊b/2⌋⋅ab=2k+1,k∈Z
  • 边界条件:a0=001;
  • 时间复杂度:O(log⁡b)。
cpp
typedef long long LL;

LL Pow(LL a, LL b) {
    if (!b) return 1; // 边界条件
    LL res = Pow(a, b / 2);
    if (b % 2 == 0)   // b 是偶数
        return res * res;
    else              // b 是奇数
        return res * res * a;
}

递推写法 ​

若 b 在二进制下的第 k1,00k2,00k3,00⋯ 位为 1,则 b=002k1+002k2+002k3+00⋯(ki≤log⁡b)。

举个例子,(14)10=00(1011)2,二进制下 14 的第 0,001,003 位都为 1,那么 14=0020+0021+0023。

将 b=002k1+002k2+002k3+00⋯ 代入 ab:

ab=a(2k1+2k2+2k3+⋯)=a2k1⋅a2k2⋅a2k3⋅ ⋯

扫描 b 的每个二进制位,如果第 k 位是 1,则将答案乘上 a2k。

时间复杂度为 O(log⁡b)。

cpp
typedef long long LL;

LL Pow(LL a, LL b) {
    LL res = 1;
    while (b) {
        if (b % 2 == 1) // 二进制下该位为 1
            res *= a;
        a *= a, b /= 2;
    }
    return res;
}

更多时候,需要求对 m 取模的快速幂结果,那么需要全程取模:

cpp
typedef long long LL;

LL Pow(LL a, LL b, LL m) {
    LL res = 1;
    while (b) {
        if (b & 1) // 等价于 b % 2 == 1
            res = (res * a) % m;
        a = (a * a) % m;
        b >>= 2;
    }
    return res;
}