(这篇其实是我用来练习公式编辑器滴,所以讲的内容略水,大佬们也赏脸看看吧)
定义
快速幂即快速求幂(下文为求a的x次幂模m的结果),但我们一般只在要求对一个数的幂取模时才使用,因为有可能结果很大,有可能long long都存不下,但是因为我们有:
\((ab)\%m=(a\%m)(b\%m)\)
通过转换,可得:
\((a^x)\%m\)
\(= (a\times a\times a\times …\times a(共x个a相乘))\%m\)
\(= (a\%m)\times (a\times a\times a\times …\times a(共x-1个a相乘))\%m\)
\(=((a\%m)\times a\%m)\times (a\times a\times a\times …\times a(共x-2个a相乘))\%m\)
\[……\]
按照这个规律可以一直分解下去,有点类似递归的思想,这样对没一次乘都取模,计算的过程中就不会内存爆炸了
但是只按照上面的方法分解时间复杂度是\(O(x)\),明显不够优秀(摊手手),快速幂一般那个\(x\)都是极其变态滴,比如x都是\(10^8\)级别的,或者有时候还要求很多次的快速幂,这个复杂度明显凉了,但是我们正统的快速幂算法只要\(O(log(x))\)的时间复杂度,这样就算x真的取\(10^8\)也才\(O(27)\)是不是贼快,接下来我就来介绍一下怎么实现
思路
先来说说递归的思路:
int modpow(int a,int x,int mod){……}
表示求a的x次幂,结果对mod取这个算法的核心就是用平方的方法来省事
如果x为偶数,设\(x=2\times k\),
则 \(a^x=a^{2k}=(a^k)^2\)
然后递归调用modpow(a,x/2,mod)就可以得到\(a^k\),
定义一个新变量(比如tmp)把这个结果存储起来
然后tmp=(tmp\times tmp)%mod;
此时的tmp就是\(a^{2k}\)即\(a^x\)
如果x为奇数,设\(x=2\times k+1\),
则 \(a^x=a^{2k+1}=(a^k)^2\times a\)
\(a^{2k}\)还是要求的,所以上面的步骤还是要做(因为是整除,所以x/2在这里于是等于k)
所以可以在上面的步骤做完后判断一下
if(x%2==1)//即x为奇数
tmp=(tmp*a)%mod;
特别的,如果x为0,立刻返回1(这个肯定没毛病吧)
于是我们的快速幂递归版就闪亮登场了:
int modpow(int a,int x,int mod)//求a的x次幂的递归法,结果对mod取模
{
if(x==0)
return 1;
int tmp=modpow(a,x/2,mod);
tmp=(tmp*tmp)%mod;//这里tmp就已经是a的2k次幂
if(x%2==1)//如果x是奇数
tmp=(tmp*a)%mod;
return tmp;
}
然后是非递归思路:
这个方法比较玄学,可以参考一下(我还是比较喜欢递归法)
核心思路是这样的,我们把x转换为二进制,答案初值赋为1,然后如果从右往左数第k位是1,就把答案乘上\(a^{k^2}\),同时在枚举二进制下的x的位数时可以顺便把\(a^{k^2}\)搞了,可以用一个while实现以上步骤,复杂度同样是\(O(log(x))\)(是不是听不懂,没关系看看代码吧):
int poww(int a,int x,int mod){//求a的x次幂的非递归法,结果对mod取模
int ans=1,base=a;
while(x>0){
if(x%2==1)//如果当前这位为1
ans=(ans*base)%mod;
base=(base*base)%mod;//这里处理的是a的2k次幂
x/=2;//这样下一次处理的就是x在二进制下的下一位
}
return ans;
}
OK这篇模板讲解就到这咯,一般不会有题目会要求直接求快速幂,可能会换个马甲,也有时候会是一道综合的题目里一个小部分,反正就是不用当心求a的x次方时会超时或超内存,快速幂很优秀
原文地址:https://www.cnblogs.com/fjyyc/p/9215603.html