前言,高次幂的一般计算:
求 a^b % m的值,时间复杂度O(b)
利用for来写,每一次循环都 mod m (利用模运算—>可在网站首页搜索"模运算"了解同模运算加减乘除定理)
结果:b接近于10亿时,计算机运算时间接近于18秒
快速幂的简单介绍:
快速幂的递归写法
前言:
我们知道,3^4= 3^2 * 3^2 = 3^1 * 3^1 * 3^1 * 3^1 ;
那么我们如果先将32的值计算出来,储存在临时变量p中,那么此时,34中需要计算的量就为:
3^4= 3^2 * 3^2 = 3^1 * 3^1 * 3^1 * 3^1 ;–>只计算了两次
此时a^b运算的时间复杂度 从O (b) 变为了 O (logb)
实现方式,递归思想;
注意点:当出现3^5这种指数为奇数的情况时,我们可以写成 3^1 * 3^4 的形式,即当ab,b为奇数时,我们将其写成a(b-1) * a 的形式
递归代码:
typedef long long ll;
int ans;
int quick_pow(int base,int power,int mod)//base为底数,power为指数
{
ll p;
if(power==0)
return 1;
if(power%2)
return base%mod*quick_pow(base,power-1,mod)%mod;
else
{
p=quick_pow(base,power/2,mod)%mod;
return p*p%mod;
}
}
如何将递归形式简化写成while()形式:
我们知道 例如3^8= 3^4 * 3^4 其实可以写成 38=(34)2=(32)4,即当ab,b为偶数时,我们可以写成(a2)(b/2),将上述的quick_pow(base,power/2,mod) * quick_pow(base,power/2,mod),简化为,a=a * a后,令b/=2;
且当b为奇数时,a^b=a * a^(b-1)—>这个a并不是初始的底数而是当前的a,将此时的a用ans=ans * a将其“收集”起来,而最终,b为1时,也会有ans=ans * a,所以最终ans为答案
举例:a^7=a * a^6=a * (a2)3 =a * a^2 * (a2)2=… 其中 其一步 ans将 a 收集起来 第二步 将 a^2 收集起来…
while()形式代码:
int quick_pow(ll base,ll power,int mod)
{
ll ans=1;
while(power!=0)
{
if(power&1)
ans=ans*base%mod,power--;
else
base=base*base%mod,power/=2;
}
return ans;
}
注意这里利用了位运算&,在这里的作用为判读奇偶。
为什么使用位运算:位运算消耗的时间更短。
位运算的其他用法请参考:
https://blog.csdn.net/weixin_42216574/article/details/82885102
快速幂的迭代形式:
例:a ^13 = a^1 * a^4 * a^8,是因为13的二进制 1101。
所以我们可以将a^b 表示成 ∏a(2符合条件的i) (∏为连乘)。若b二进制中i+1位为1则符合条件。
且由于每进一位,a->a2->(a2)^2->。。。
所以我们可以先定义一个ans,从b的二进制第一位(最右边)开始,若a&b为1,则说明,这一位为1,那么ans * =a,否则,不乘,进入下一位,实行操作a=a * a b>>=1; 直到b取尽。
代码
int quick_pow(ll base,ll power,int mod)
{
ll ans=1;
while(power>0)
{
if(power&1)//判断二进制下第一位是否为1
ans=ans*base%mod;
base=base*base%mod,power>>=1;
}
return ans;
}
如此一来,进一步降低了复杂度,使得快速幂更加高效。