快速幂学习笔记
背景:给定\(a\),\(b\),\(m\),求 \(a^b \mod m\)
常规方法:
\(O(n)\)的时间复杂度,循环一遍即可,程序如下(用pow函数也可):
#include <iostream>
using namespace std;
int a,b,m;
int ans=1;
int main() {
cin>>a>>b>>m;
for(int i=1;i<=b;i++)
ans*=a;
cout<<ans%m;
return 0;
}
当然,如果\(a\),\(b\)足够大时,上述程序时肯定不行滴
所以引进我们的\(dalao\):
STL快速幂
比如说:求\(4^{10}\)
常规方法
\(4^{10}=4\times4\times4\times4\times4\times4\times4\times4\times4\times4\),一共\(10\)次
那快速幂呢?
\(4^{10}=4^8\times4^2=(4^4)^2\times(4^1)^2=((4^2)^2)^2\times(4^1)^2=(((4^1)^2)^2)^2\times(4^1)^2\)
原理:
将指数\((10)_{10}\)转化为\((1010)_2\)
然后\((1010)_2\) 等价于 \(2^3+2^1\)
所以\(4^{10}=4^{2^3}\times4^{2^1}=4^8\times4^2\)
为什么要这么做?
因为只需要乘4次!!!!!!
第1次;\(4^2\)由\(4^1\times4^1\)即由\(4\times4\)得到
第2次;\(4^4\)由\(4^2\times4^2\)得到
第3次:\(4^8\)由\(4^4\times4^4\)得到
第4次:\(4^{10}\)由\(4^8\times4^2\)得到,结束
而且,位运算快!
所以时间复杂度就由 \(O(n)\) 降到了 \(O(\log n)\),岂不美哉?
所以接下来,我们就得写代码了:
可以说这个模板是最简单的?
// a^b%m 模板
int qmi(int a,int b,int m){
int res=1;
while(b){
if (b&1) res=1LL*res*a%m; // b&1 等价于 b%2==1
a=1LL*a*a%m;
b>>=1; // b/=2
}
return res;
}
首先,int res=1;不必说,\(res\)为结果
然后while(b){......} 相当于拆分指数 \(b\)
然后……敲黑板!!!
-
if (b&1)实乃精华为什么?
举个例子吧
比如指数\(b=(26)_{10}=(11010)_2\)
接着:\(b\& 1\)进行与运算
即为:\(11010 \& 00001=0\),为假,所以不执行
res=res*a;这个时候,有没有发现什么?
正好与\((11010)_2\)的最后一位相符
如果再执行下面的
b>>=1;,再返回回来执行依然如此所以我们可以发现:
b&1等价于b%2==1
再然后,执行res=res*a;,即为:\(4^2\)由\(4^1\times4^1\)即由\(4\times4\)得到
下一步:
目标:
\(4^4\)由\(4^2\times4^2\)得到
这里呢,我们发现:\(4^1\)变成了\(4^2\),即\((4^1)^2\)
所以得有一句:a=a*a;
再然后b>>=1 ,右移一位,进行下一轮操作
最后循环完毕,返回res
但这里还得注意几点:
- 别忘了\(\mod m\)
- 为防止越界,最好乘上
1LL
所以res=res*a; a=a*a;得分别变成res=1LL*res*a%m; a=1LL*a*a%m;
所以,结束了?再放一遍程序吧
// a^b%m 模板
int qmi(int a,int b,int m){
int res=1;
while(b){
if (b&1) res=1LL*res*a%m; // b&1 等价于 b%2==1
a=1LL*a*a%m;
b>>=1; // b/=2
}
return res;
}

浙公网安备 33010602011771号