Hello wo|

Weekoder

园龄:1年1个月粉丝:2关注:0

快速幂

大家好,我是Weekoder!

今天的内容是快速幂!(实际上是为了讲矩阵快速幂赶出来的嘻嘻

Part 1 用处

快速幂,顾名思义就是快速地计算出某个数的幂,形如 an

Part 2 思想

为什么普通的幂运算慢?假设要计算 an,则需要拆分成 a×a××a×a,运算 n 次,复杂度为 O(n)。当 n 很大时,这个算法明显就不行了。那要怎么优化呢?我们先来看一个简单一点的例子:当 n2 的幂时,可以怎么做呢?假设 n32,可以这样计算:

a1×a1=a2a2×a2=a4a4×a4=a8a8×a8=a16a16×a16=a32

注意:ax×ay=ax+y

可以发现,这样只计算了 5 次,相比于朴素算法的 32 次,将时间复杂度优化到了 O(logn)。这其实是倍增的原理,相比于一个一个乘 a,不如将 a 的数量翻倍乘。

那如果 n 不是 2 的幂呢?比如,n=105 的时候,该怎么办呢?虽然 105 不是 2 的幂,但是我们发现 105 可以拆分成 2 的幂之和,像这样:

105=1+8+32+64

于是,我们可以把 a105 拆分一下:

a105=a1+8+32+64=a1×a8×a32×a64

我们在刚刚提到过,计算 n2 的幂的情况是很容易的,所以我们只需要将它们相乘即可。

这个问题的关键在于,怎样将一个数拆分成 2 的幂之和?我们来看一下他们在二进制下的样子:

可以看到,105 的二进制中有 41,而 2 的幂的数都只有一个 1,并且刚好和 105 的四个 1 位置一样。所以,只要将 105 二进制中的 1 拆开,就能得到 2 的幂的数字是哪些了。

而因为一个数 n 的二进制最多只有 logn 位,所以时间复杂度为 O(logn)

Part 3 实现

就决定是你了!快速幂模板

先上代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

ll expow(ll a, ll n, ll p) {
    ll r = 1;
    while (n) {
        if (n & 1) r = r * a % p;
        a = a * a % p, n >>= 1;
    }
    return r;
}

int main() {
    ll a, b, p;
    cin >> a >> b >> p;
    cout << a << "^" << b << " mod " << p << "=" << expow(a, b, p);
    return 0;
} 

输入和输出就不用我讲了,重点是 expow 函数。我把快速幂函数提取出来(先不取模):

typedef long long ll;

ll expow(ll a, ll n) {
    ll r = 1;
    while (n) {
        if (n & 1) r *= a;
        a *= a, n >>= 1;
    }
    return r;
}

比如计算 7105

首先,我们用一个 result 来存储结果,初始时为 1。接着,有一个 while 循环,其实就是遍历 n 在二进制下的每一位。如果当前这一位是 1,即 n1 按位与的结果为 1,则可以拆分,将 r 乘上 a。每过一位,a 就变为 a2,即模拟倍增的过程。然后还要将 n 除以 2,用位运算表示就是右移一位,获取下一位。最后返回结果 r。可以辅助图片理解。

这样就可以用 O(logn) 的速度计算 an 了。

小扩展:幂取模

即计算 anmodp

只需要在快速幂的模板里稍微改动一下。在做乘法运算时,顺带取模就行了。

幂取模模板代码如下:

typedef long long ll;

ll expow(ll a, ll n, ll p) {
    ll r = 1;
    while (n) {
        if (n & 1) r = r * a % p;
        a = a * a % p, n >>= 1;
    }
    return r;
}

Part 4 小结

综上所述,二进制快速幂的核心就是这些了。当然,快速幂除了计算 an 以外,还有很多用处等着你去发现。

再见!

本文作者:Weekoder

本文链接:https://www.cnblogs.com/Weekoder/p/18237773

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Weekoder  阅读(36)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起