⎨⎧a⋅an−1,a2n⋅a2n,1,if n is oddif n is even but not 0if n is 0
可以得到一个递归算法:
1 2 3 4 5 6 7 8 9 10 11 12
typedeflonglong ll; int MOD = 1e9 + 7; ll pow2(ll a, ll n) { if(n == 0) // n is 0 return1; if(n & 1) // n is odd return ((pow2(a, n - 1) * a) % MOD); // n is even but not 0 ll t = pow2(a, n >> 1); return (t * t) % MOD; }
时间复杂度O(logn) ,空间复杂度O(logn) 。
非递归快速幂
假设 n = 10,10 的二进制为 1010B, 则a10=a(1010)2=a(1000)2⋅a(10)2=a23⋅a21
我们遍历 n 的二进制(从低位到高位),如果 n 的二进制的第 t 位为 1,则让结果乘以a2t ,而将a2t 转化为a2t+1 只需要计算一次平方:a2t+1=(a2t)2 ,那么就可以使用循环将计算an 的时间复杂度降低为O(logn) (n 的二进制的位数为⌊log2n⌋+1):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedeflonglong ll; int MOD = 1e9 + 7; ll pow3(ll a, ll n) { ll rst = 1; while (n) { // 第 i 次循环时,n 的二进制的最后一位为函数调用时的 n 的二进制的第 i 位,i 从 0 开始计 // 第 i 次循环时,a 已经成为函数调用时的 a 的 2^i 次方 if (n & 1) // 若函数调用时的 n 的二进制中第 i 位为 1 rst = (rst * a) % MOD; // rst *= a ^ (2 ^ i),注意代码中的 a 已经不是最开始调用函数时的 a 了 a = (a * a) % MOD; // a ^ (2 ^ (i + 1)) = (a ^ (2 ^ i)) ^ 2 n >>= 1; // 以便下一个循环中,取 n 的二进制的下一位 } return rst; }
在计算an 时,若 a 的类型 a 支持 乘法,并且满足 乘法结合律 (因为快速幂改变了乘法的运算顺序)便可以使用快速幂,例如传说中的矩阵快速幂。
实现方式与之前的方法非常类似。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
typedeflonglong ll; template <typename T> T powT(T a, ll n) { T ans = 1; // 赋值为乘法单位元,可能要根据构造函数修改,例如数字的乘法单位元为 1,矩阵的乘法单位元为单位矩阵 while (n) { if (n & 1) ans = ans * a; a = a * a; n >>= 1; } return ans; }