AcWing 875. 快速幂

题目传送门

一、概念与原理

快速幂:快速求出 \(a^k\ mod\ p\) 的值, 比如 \(2^{100} \% 7\) 。

快速幂算法的原理是通过将指数拆分成几个因数相乘的形式,来简化幂运算。在我们计算\(3^{13}\) 的时候,普通的幂运算算法需要计算\(13-1\)次,但是如果我们将它拆分成\(3^{8+4+1}\) ,只需要计算\(4\)次。嗯?哪来的\(4\)次?,别急,接着看。

这种拆分思想其实就是借鉴了二进制与十进制转换的算法思想,我们知道\(13\)的二进制是\(1101\),可以知道:
\(13=1\times2^{3} + 1\times2^{2} + 0\times2^{1} + 1\times2^{0} = 8 + 4 + 1\)

原理就是利用位运算里的位移“>>”和按位与“&”运算,代码中 \(k \& 1\)其实就是求\(k\)对应二进制数字的最低位是\(0\)还是\(1\),再根据是\(0\)还是\(1\)决定乘不乘,不理解的话联系一下二进制转换的过程。\(k >>= 1\)其实就是将\(k\)的二进制向右移动一位,就这样位移、取最低位、位移、取最低位,这样循环计算,整个过程和我们手动将二进制转换成十进制是非常相似的。

普通幂算法是需要循环指数次,也就是指数是多少就要循环计算多少次,而快速幂因为利用了位移运算,只需要算“指数二进制位的位数”次,对于\(13\)来说,二进制是\(1101\),有\(4\)位,就只需要计算\(4\)次,快速幂算法时间复杂度是\(O(log_n)\)级别,对于普通幂需要计算一百万次的来说,快速幂只需要计算\(6\)次,这是速度上质的飞跃,但是需要多注意溢出的问题。

二、同余的性质

性质1:
\((a + b)\% p = (a\% p + b\% p)\% p\)

性质2:
\((a - b)\% p = (a\% p - b\% p )\% p\)

性质3:
\((a * b)\% p = (a\% p * b\% p)\% p\)

这里需要注意的是,除法可没有这个性质,那样用逆元来运算了!

三、完整代码

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
int n;


// 快速幂取模 (a^k)%p
LL qmi(int a, int k, int p) {
    LL res = 1;
    while (k) {
        if (k & 1) res = (LL) res * a % p;
        k >>= 1;
        a = (LL) a * a % p;
    }
    return res;
}

int main() {
    //读入优化
    ios::sync_with_stdio(false);
    cin >> n;
    while (n--) {
        int a, k, p;
        cin >> a >> k >> p;
        printf("%d\n", qmi(a, k, p));
    }
    return 0;
}
上一篇:返回键的javascript sort函数


下一篇:软件工程 教材 知识点 思维导图