快速幂模板及讲解
Posted fjyyc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速幂模板及讲解相关的知识,希望对你有一定的参考价值。
(这篇其实是我用来练习公式编辑器滴,所以讲的内容略水,大佬们也赏脸看看吧)
定义
快速幂即快速求幂(下文为求a的x次幂模m的结果),但我们一般只在要求对一个数的幂取模时才使用,因为有可能结果很大,有可能long long都存不下,但是因为我们有:
((ab)\%m=(a\%m)(b\%m))
通过转换,可得:
((a^x)\%m)
(= (a imes a imes a imes … imes a(共x个a相乘))\%m)
(= (a\%m) imes (a imes a imes a imes … imes a(共x-1个a相乘))\%m)
(=((a\%m) imes a\%m) imes (a imes a imes a imes … imes 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 imes k),
则 (a^x=a^{2k}=(a^k)^2)
然后递归调用modpow(a,x/2,mod)就可以得到(a^k),
定义一个新变量(比如tmp)把这个结果存储起来
然后tmp=(tmp imes tmp)%mod;
此时的tmp就是(a^{2k})即(a^x)
如果x为奇数,设(x=2 imes k+1),
则 (a^x=a^{2k+1}=(a^k)^2 imes 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次方时会超时或超内存,快速幂很优秀
以上是关于快速幂模板及讲解的主要内容,如果未能解决你的问题,请参考以下文章