快速幂
顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
快速幂的思想
假设一个情况,我们要求 511 的具体值。利用常规思路,即为
5
∗
5
∗
5
∗
5
∗
5
∗
5
∗
5
∗
5
∗
5
∗
5
∗
5
=
48828125
5*5*5*5*5*5*5*5*5*5*5=48 828 125
5∗5∗5∗5∗5∗5∗5∗5∗5∗5∗5=48828125这样的操作在算法内即通过一个循环可以完成,时间复杂度为
O
(
n
)
O(n)
O(n)。
但是,快速幂采用了一种更巧妙,且更快的思路。我们将 511 做如下变换
5 23 + 21 + 20
接下来我们首先设定 a n s = 1 , a = 5 ans=1,a=5 ans=1,a=5 而后,我们看到 5 5 5 的幂中有 2 0 ( = 1 ) 2^0(=1) 20(=1),所以我们将 a n s = a n s ∗ a = 5 ans=ans*a=5 ans=ans∗a=5 ,之后将 a = a ∗ a = 5 2 = 25 a=a*a=5^2=25 a=a∗a=52=25,接下来我们发现 5 5 5 的幂中有 2 1 ( = 2 ) 2^1(=2) 21(=2),所以我们将 a n s = a n s ∗ a = 125 ans=ans*a=125 ans=ans∗a=125,之后将 a = a ∗ a = 5 4 = 625 a=a*a=5^4=625 a=a∗a=54=625,但是我们发现 5 5 5 的幂中没有 2 2 ( = 4 ) 2^2(=4) 22(=4),所以这一步不对 a n s ans ans 进行操作,直接将 a = a ∗ a = 390625 = 5 8 a=a*a=390625=5^8 a=a∗a=390625=58,接着我们发现 5 5 5 的幂中有 2 3 ( = 8 ) 2^3(=8) 23(=8),所以我们将 a n s = a n s ∗ a = 48828125 ans=ans*a=48828125 ans=ans∗a=48828125,得到了我们的答案。数一数,在上述过程中,一共经过了 6次运算 ,相较于原先的 11次运算 有了明显的改进,因为快速幂的思想就是将之前获得的值再次利用,避免重复计算,于是可以降低时间复杂度。
那么还有一个问题,如何快速实现
11
=
2
3
+
2
1
+
2
0
11=2^3+2^1+2^0
11=23+21+20的转变呢?,其实这里运用到了二进制和十进制的转换,
11
11
11 在二进制中为
1011
1011
1011,再运用二进制转十进制的想法我们就可以获得
2
3
+
2
1
+
2
0
2^3+2^1+2^0
23+21+20 这一式子,在计算机中,可以通过运算符>>
和<<
快速实现这一思想,以及上述的是否存在幂的比较过程,下面将在代码中体现。
代码
//由于快速幂后数值可能过大,所以往往伴随取模运算
ll mod = 10e9 + 7;
ll qpow(int a, int n) {
ll re = 1;
while (n) {
if (n & 1) {
re = (re*a) % mod;
}
n >>= 1;
a = (a*a) % mod;
}
return re%mod;
}
快速幂的其他运用
逆元
这里转载一个博客:https://blog.sengxian.com/algorithms/mod-world
其对于模运算的总结以及其中涉及到的逆元十分详尽
逆元常常被运用于分数取模中,注意分数取模和整数是不同的,因为取模和取余的意义就有很大不同,但常常容易弄混,在了解了逆元后,对于分数取模的意义会有更加深刻的理解。
例题(HDU - 6595)http://acm.hdu.edu.cn/showproblem.php?pid=6595
当然此题的关键在于如何获得期望公式,而非取模。
这题在获得期望公式后,剩下的操作就是取模,在取模的过程中则会运用到快速幂来求解逆元,最后输出答案,由于是分数取模,所以可以看到样例中的两个输出都会有很大的值。