快速幂:快速求 a ^ b % p(这三个数的数量级都可以为10^9)的问题,时间复杂度:O(logb)
首先要知道模运算有一个重要的规律:
(a * b) % p = (a % p * b % p) % p
由这个公式,可以将计算的过程分为三步:
①先分解出a^b 的一部分因数(具体是哪部分下文会分析);
②将这些因数分别对p取模得到一系列数;
③然后将这些数相乘的结果p取模即可得出结果。
在代码运行过程中,决定运算效率的关键就是如何快速分解出 a ^ b 的一部分因数,如果是分解成b个a,运算的时间复杂度就会高达O(b)。那么,有什么方式是既方便运算,又可以将a^b分解为足够少的因数呢?
此处使用的方法是将a^b分解为:\(k_0\) * \(a^{2^{0}}\) * \(k_{1}\) * \(a^{2^{1}}\) * \(k_{2}\) * \(a^{2^{2}}\) *...* \(k_{2^{\log_{2}b}}\) * \(a^{2^{\log_{2}b}}\)(其中\(k_{n}\)均为0或1),也就是将b分解为 \(k_{0}\) * \(2^{0}\) + \(k_{1}\) * \(2^{1}\) + \(k_{2}\) * \(2^{2}\) +...+ \(k_{2^{\log_{2}b}}\) * \(2^{\log_{2}b}\) 。
这种分解方式的计算十分简单,只需要计算b的二进制表示,即可求出\(k_{n}\)(n=0,1,2,...,\(2^{\log_{2}b}\))的值。
同时,分解出的因数进行模p运算也非常简单:因为分解出的每一项\(a^{2^{n}}\)都是上一项\(a^{2^{(n-1)}}\)的两倍。所以:计算要 \(a^{2^{n}}\) % p ,只需要计算\( ( a^{2^{(n-1)}} \)% p ) * \( ( a^{2^{(n-1)}}\) % p ) % p 而无需计算出\(a^{2^{n}}\)本身的值。由第一项开始进行递推,即可在一次循环内完成整个运算过程!
在实际的代码中,我们可以发现:“计算b的二进制表示”,和“将分解出的因数进行模p运算”,可以同时进行,进一步简化流程。
c++的代码如下:
/*
输入:a,b,p
输出:a^b%p
*/
#include<iostream>
using namespace std;
long long int qmi(long long int a, long long int b, long long int p) {
long long res = 1;
while (b) {
//如果b的二进制的第0位是1,就表示kn为1,更新当前余数
if (b & 1) res = res * a % p;
b /= 2;
a = a * a % p;
}
return res;
}
int main() {
long long int a, b, p;
cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
return 0;
}