以前我看快速幂真的看不懂,但是当我慢慢对递归(递归大法好)有一些理解后,对二分又理解一些后,就能够自己写出快速幂的递归写法了。
求 a^b % m的值,这个用普通算法我就不说了,时间复杂度O(b)
当我知道快速幂之后。才发现 a ^ b还能这样算,太秀了吧,当然你必须带一些二分和递归,不然你看不懂它的递归式。会想(这TM是What ??? )数学之美就是你在能不断刷新你的认知,还TM能这样,太秀了吧,然后你一跺脚,一拍手,就学会了。手动@狗头。
神奇的快速幂,时间复杂度O(logb).
我们已知 2^3 求 2^6,不就是 2^3 * 2^3嘛。快速幂就是这个原理。
那有同学问了遇到奇数怎么办?2 ^ 5??
那不就是 2 * 2 ^ 4 这不就成了嘛。
所以这就是快速幂的基本思路求a ^ b
1)当b是奇数时,那么有 a^b = a * a^*(b-1)
2)当b是偶数时,那么有 a^b = a^(b/2) * a^(b/2)
举个例子?2 ^10
- 2^10 = 2^5 * 2^5
- 2^5 = 2 * 2^4
- 2^4 = 2^2 * 2^2
- 2^2 = 2^1 * 2^1
- 2^1 = 2 * 2^0
根据这两个条件写递归式嘛,这还不简单?
typedef long long ll;
ll binaryPow(ll a, ll b, ll m){
if(b == 0)
return 1;
else if(b % 2 == 1)
return a * binaryPow(a, b - 1, m) % m;
else{
ll num = binaryPow(a, b/2, m) % m; //优化
return num * num % m;// 不直接写成return binaryPow(a, b/2, m) * binaryPow(a, b/2, m)
}
}
上述
if(b % 2 == 1)
可以改为
if(b & 1)
因为b & 1 是按位与,判断b的末尾是否为1 ,因此当b 为奇数时 b & 1 返回为1,if条件成立,这样执行速度更快
+-一般使用2个CPU时钟
位运算 只要1个
* 要4个
/ 要40个
那我们是不是要学习一下位运算:位运算
针对不同的题目,有两个细节需要注意
1)如果初始值a 大于 m ,那么需要在进入函数前就让a 对 m 取模,
2)若果m 为 1,可以直接在函数外部特判为 0,不需要进入函数来计算。(因为任何数对1 取模都是0。
快速幂的迭代写法
研究一下快速幂的迭代写法。
对于 a ^ b来说,若果把 b 写成2 进制,那么b 就可以写成若干二次幂之和,如13 的二进制 1101,于是3 号位 、2号位、0号位就都是1,那么就可以得到13 = 2^3 + 2^2 + 2^1 = 8 + 4 + 1。所以a ^13 = a^8 * a^4 * a^1。
通过同样的推导,我们可以把任意的a^b 表示成 a^(2^k)……、a^8、a^4、a^2、a^1中若干的乘积。若果二进制的i号位为1.那么想中的a^(2^i)就被选中。于是可以得到计算a^b的大致思路:令i 从0到k枚举b的二进制的每一位,如果为1 那就累计a^(2^i)。注意
a^(2^k)……、a^8、a^4、a^2、a^1前一项总是等于后一项的平方。具体步骤。
(1)初始令ans = 1,用来存放累积的结果。
(2)判断b的二进制末尾是否为1 ,(及判断 b&1 是否为 1),也可以理解为判断b 是否为奇数。如果是的话,令ans乘上a的值。
(3)令a平方,并使b右移一位,(也可以理解为,b/2)
(4)只要b 大于0,就返回(2)。
typedef long long ll
ll binaryPow(ll a, ll b, ll m){
ll ans = 1;
while(b > 0){
if(b & 1){
ans = ans * a % m;
}
a = a * a % m;
b >>= 1;
}
return ans;
}
例:a^13
b | b&1 | ans | a |
1 | a | ||
1101 | 1 | 1*a=a | a^2 |
110 | 0 | a | a^4 |
11 | 1 | a*a^4 = a^5 | a^8 |
1 | 1 | a^5 * a^8 = a ^ 13 |
总结
我认为递归更好记,迭代还可以,但是我们知道了 我们需要补习一下位运算。
咦,递归和2分好像啊。。。。递归如果以中间作为中点递归,感觉就是二分的一种。