快速幂算法详解

快速幂算法详解

前言

首先考虑这么一个问题

{% note info %}

给定三个正整数 a, b, m(a < 1 0 9 10^9 109,b < 1 0 9 10^9 109,1 < m < 1 0 9 10^9 109),求 a b a^b ab % m。

{% endnote %}

对于这个问题,只要写一个简单的循环就能够搞定

// 普通求幂
long long QuickPow(long long a, long long b, long long m) {
    long long ans = 1;
    for (int i = 0; i < b; i++) {
        ans = ans * a % m;
    }
    return ans;
}

然而,当 a, b 到达一定值时,最终的结果会非常大,对于这个问题,O(b)的时间复杂度很难进行。

快速幂算法

快速幂,就是用效率更高(时间复杂度更低)的方法求幂,可以将时间复杂度优化至 O(logn)

递归快速幂

快速幂算法的关键在于对指数 b 的处理,我们很容易得到如下事实:

若 b 为奇数,则 a b = a × a b − 1 a^b=a\times a^{b-1} ab=a×ab1

若 b 为偶数,则 a b = a b / 2 × a b / 2 a^b=a^{b/2}\times a^{b/2} ab=ab/2×ab/2

举个例子,求 2 7 2^7 27

  1. 由于 7 为奇数,因此有 2 7 = 2 × 2 6 2^7 = 2 \times 2^6 27=2×26
  2. 由于 6 为偶数,因此有 2 6 2^6 26 = 2 3 × 2 3 2^3 \times 2^3 23×23
  3. 由于 3 为奇数,因此有 2 3 2^3 23 = 2 × 2 2 2 \times 2^2 2×22
  4. 由于 2 为偶数,因此有 2 2 2^2 22 = 2 1 × 2 1 2^1 \times 2^1 21×21
  5. 由于 1 为奇数,因此有 2 1 2^1 21 = 2 × 2 0 2 \times 2^0 2×20
  6. 最后 2 0 = 1 2^0=1 20=1,然后从下往上计算即可

根据上面的方程,很容易通过二分的思想得到快速幂算法的递归版本

// 快速幂,递归写法
long long QuickPow(long long a, long long b, long long m) {
    if (b == 0) return 1;
    if (b % 2 == 1)  // 如果 b 为奇数,转化为 b - 1
        return a * QuickPow(a, b - 1, m) % m;
    else {  // 如果 b 为偶数,转化为 b / 2
        long long mul = QuickPow(a, b / 2, m);
        return mul * mul % m;
    }
}

迭代快速幂

下面说明一下快速幂的迭代写法

同样的,主要还是在指数 b 的处理,以 2 10 2^{10} 210 为例

10 = ( 1010 ) 2 = 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 0 × 2 0 10=\left( 1010 \right) _2=1\times 2^3+0\times 2^2+1\times 2^1+0\times 2^0 10=(1010)2=1×23+0×22+1×21+0×20

2 10 = 2 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 0 × 2 0 = 2 8 + 0 + 2 + 0 = 2 8 × 2 2 2^{10}=2^{1\times 2^3+0\times 2^2+1\times 2^1+0\times 2^0}=2^{8+0+2+0}=2^8\times 2^2 210=21×23+0×22+1×21+0×20=28+0+2+0=28×22

可以发现, 2 10 2^{10} 210 可以表示为 2 8 2^8 28 2 2 2^2 22 的乘积。

同样的对 a b a^b ab 进行推广,我们可以把 a b a^b ab 表示成 a 2 k a^{2^{k}} a2k a 2 k − 1 a^{2^{k-1}} a2k1,…, a 8 a^8 a8 a 4 a^4 a4 a 2 a^2 a2 a 1 a^1 a1中若干项的乘积,即:

a b = a c k ⋅ 2 k × a c k − 1 ⋅ 2 k − 1 × ⋯ × a c 3 ⋅ 8 × a c 2 ⋅ 4 × a c 1 ⋅ 2 × a c 0 ⋅ 1 a^b=a^{c_k\cdot 2^k}\times a^{c_{k-1}\cdot 2^{k-1}}\times \cdots \times a^{c_3\cdot 8}\times a^{c_2\cdot 4}\times a^{c_1\cdot 2}\times a^{c_0\cdot 1} ab=ack2k×ack12k1××ac38×ac24×ac12×ac01

具体来说,枚举指数 b 的每一位,若当前位为 1,则结果累计 a 2 i a^{2^{i}} a2i

举例如下

an(binary)ans
2(1)210101
2(10)21012(10)2
2(100)2102(10)2
2(1000)212(10)2 * 2(1000)2

具体代码实现如下:

// 快速幂,迭代写法
long long QuickPow(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
        if (b & 1) {            // 若 b 的二进制末尾为 1(也可以写成 if(b % 2))
            ans = ans * a % m;  // ans 累加上 a
        }
        a = a * a % m;  // a取平方
        b >>= 1;        // b 的二进制右移一位(也可以写成 b /= 2)
    }
    return ans;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EmoryHuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值