【概述】
快速幂就是利用二进制的性质来快速计算底数的 n 次幂,时间复杂度为 O(log₂N), 与朴素的 O(N) 相比效率有了极大的提高。
但有时指数十分大,利用高精来写可能会超时,此时就需要十进制快速幂。
【求位数公式】
有时仅要求求 2 的 n 次幂的位数,这里有一个神奇的求位数公式:floor(log(2)/log(10)*n+1)
double res=n*log10(2)+1;
cout<<int(res)<<endl;
【二进制快速幂】
以求 a 的 b 次方为例
将 b 转换成二进制数,该二进制数第 i 位的权为:
例如:
11 的二进制是 1011,即:11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1
因此,我们将计算 a¹¹ 转化为计算
递归公式:
可借助位运算来实现:
- b & 1 //取 b 二进制的最低位,判断和 1 是否相同,相同返回 1,否则返回 0,用于判断奇偶
- b>>1 //把 b 的二进制右移一位,即去掉其二进制位的最低位
int quickPow(int a,int b){
int res=1;
while(b){
if(b&1)
res*=a;
a*=a;
b>>=1;
}
return res;
}
【二进制快速幂取模】
所谓二进制快速幂取模,就是计算给出 a,b,m 三个数,快速计算 的值
分析:以求 为例
因此,可递归的对 n 进行二进制分解,在进行快速幂运算的同时进行求模。
int powMod(int a, int b, int m){
int res=1;
while(b){
if(b&1)
res=(res*a)%m;
a=(a*a)%m;
b>>=1;
}
return res;
}
【快速乘法的二进制快速幂取模】
当数非常大时,计算 (a*b)%m 使用 long long 也有可能爆精度,此时需要利用快速乘法,将转乘法为加法,在模拟的同时不断求模
LL multMod(LL a,LL b,LL m){//res=(a*b)%m
a%=m;
b%=m;
LL res=0;
while(b){
if(b&1)
res=(res+a)%m;
a=(a<<=1)%m;
b>>=1;
}
return res%m;
}
LL powMod(LL a, LL b, LL m){//res=(a^b)%m
LL res=1;
LL k=a;
while(b){
if((b&1))
res=multMod(res,k,m)%m;
k=Mult_Mod(k,k,m)%m;
b>>=1;
}
return res%m;
}
【十进制快速幂】
考虑到指数十分大的情况,例如:,此时用 python 或 Java 大数写二进制快速幂会超时,这个时候就需要用十进制快速幂来解决。
十进制快速幂与二进制快速幂没有本质区别,只是拆分指数的方法一个是以十进制,一个是以二进制。
例如:
char b[100007];
LL tenthPow(LL a,LL len,LL mod) {
LL res=1;
while(len>=0){
LL cnt=b[len]-'0';
LL cur=a;
for(int i=1; i<=cnt; i++)
res=res*a%mod;
for(int i=1; i<10; i++)
cur=cur*a%mod;
//进位
a=cur;
res%=mod;
len--;
}
return res;
}
int main(){
LL a,mod;
scanf("%lld",&a);
scanf("%s",b);//字符串读入指数
scanf("%lld",&mod);
LL len=strlen(b);
LL res=tenthPow(a,len-1,mod);
printf("%lld^%s %% %lld = %lld",a,b,mod,res);
return 0;
}