快速幂
假如我们想要得到
你可能会写出这样的代码
cpp
const int mod = 10007;
int ans = 1;
for (int i = 0; i < 100; i++)
ans = ans * 3 % mod;
return ans;
const int mod = 10007;
int ans = 1;
for (int i = 0; i < 100; i++)
ans = ans * 3 % mod;
return ans;
假如要计算的是
平方法
假设初始为
容易发现,它的指数的增长速度是
我们可以尝试利用“平方法”加速幂的运算。
递归方法
很自然的想到
可以据此实现代码
cpp
const int mod = 10007;
int qpow(int a, int n) {
if(n == 0) {
return 1;
} else if(n % 2 == 1) {
return qpow(a, n - 1) * a % mod;
} else {
int t = qpow(a, n / 2);
return t * t % mod;
}
}
const int mod = 10007;
int qpow(int a, int n) {
if(n == 0) {
return 1;
} else if(n % 2 == 1) {
return qpow(a, n - 1) * a % mod;
} else {
int t = qpow(a, n / 2);
return t * t % mod;
}
}
思考
为什么不写 qpow(a, n / 2) * qpow(a, n / 2)
,而用 t
进行存储?
递归本身也具有一定的开销,我们可以稍加思考,改进得到非递归法。
非递归法
还是讨论
那么对于任意的
注意到平方法数列中的指数都是
因为
即二进制下该位为
于是可以写出代码
cpp
const int mod = 10007;
int qpow(int a, int n) {
int ans = 1;
while(n > 0) {
if(n % 2 == 1)
ans = ans * a % mod;
a = a * a % mod;
n = n / 2;
}
return ans;
}
const int mod = 10007;
int qpow(int a, int n) {
int ans = 1;
while(n > 0) {
if(n % 2 == 1)
ans = ans * a % mod;
a = a * a % mod;
n = n / 2;
}
return ans;
}
在实际使用时往往写成这样
cpp
i64 qpow(i64 a, i64 b, i64 p) {
i64 ret = p != 1;
for(; b; b >>= 1) {
if(b & 1)
ret = a * ret % p;
a = a * a % p;
}
return ret;
}
i64 qpow(i64 a, i64 b, i64 p) {
i64 ret = p != 1;
for(; b; b >>= 1) {
if(b & 1)
ret = a * ret % p;
a = a * a % p;
}
return ret;
}
应用
快速幂只是一个小技巧,但是应用的范围很广泛。
数列递推
拿我们熟悉的 Fibonacci 数列举例
可以写成矩阵乘法形式
设
可以对矩阵使用快速幂,那么计算第