快速幂算法
一步一步优化
1.什么是快速幂算法
例题引入
A^B^ 的最后三位数表示的整数为?
正常求法
long long normalPower(long long base,long long power)
{
long long result = 1;
for(int i = 1;i <= power;i++)
{
result = result*base;
}
return result%M;
}
//T = O(n) 取决于power ,但这里会出现问题,数据会溢出
//输出结果爆掉
指数:在乘方a中,其中的a叫做底数,n叫做指数,结果叫幂。
______________________________________________________
f(x)=a^x , 随着x单位长度的递增,f(x)会呈“爆炸性”增长。
一张纸对折一次,厚度变成原来的2倍。再对折第二次,变为原来的2的2次方倍即4倍。
以此类推,假设纸的厚度为0.1mm,则对折24次以后,长度超过1千米;对折39次达55000千米,
超过地球赤道长度;对折42次达44万千米,超过地球至月球的距离;对折51次达22亿千米,
超过地球至太阳的距离;对折82次为51113光年,超过银河系半径的长度。
因此,如果题目让你求2的100次方,貌似我们程序设计语言中最大的long long类型也无法承载
这么大的数值,所以题目才不会要求你输出结果,因为结果可能会非常的大,大到没有任何类型
可以承载。所以我们会发现上面的结果为什么是0,因为已经发生溢出了
然后我们就需要用到“取模”运算的运算法则:
(1)(a + b) % p = (a % p + b % p) % p
(2)(a - b) % p = (a % p - b % p) % p
(3)(a * b) % p = (a % p * b % p) % p
利用第三条,(a*b*c)%p == (a%p*b%p*c%p)%p
化简代码如下(解决爆精度问题)
long long normalPower(long long base,long long power)
{
long long result = 1;
for(int i = 1;i <= power;i++)
{
result *= base;
result %= 1000;
}
return result%1000;
}
//T = O(n) 数据溢出解决了,但是还有问题在于时间复杂度,这里就还需要通过 n 转 log(n) 来实现
//输出时间为18s 对于 1000000000 次方
2.快速幂算法初步入门
所以就出现了下面这种方法化简计算步骤
3^10=3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3
//尽量想办法把指数变小来,这里的指数为10
3^10=(3*3)*(3*3)*(3*3)*(3*3)*(3*3)
3^10=(3*3)^5
3^10=9^5
每次都将指数的大小减半,n->n/2,前提是 n 为奇数的时候
当 n 为偶数,也不太影响,将多的一位提出来乘
代码如下
long long fastPower(long long base,long long power)
{
long long result = 1;
while(power > 0)
{
if(power%2 == 1)
{
result = result*base%1000;
}
power /= 2;
base = (base*base)%1000;
}
return result;
}
3.优化快速幂算法
power%2 == 1 可以使用 power&1 来替代,如果 p 为偶数,最后一位为 0 ,否则为 1,
所以就有 5&1 = 1,6&1 = 0
并且对于 power /= 2 来说,可以使用位运算实现减半操作 >> 1 右移一位,表示的就是变成原来的一半
代码如下
long long fastPower(long long base, long long power) {
long long result = 1;
while (power > 0)
{
if (power & 1) //此处等价于if(power%2==1)
{
result = result * base % 1000;
}
power >>= 1; //此处等价于power=power/2
base = (base * base) % 1000;
}
return result;
}
所以说,所有的加减乘除运算均可使用位运算替代
简写代码如下
typedef long long ll
ll ksm(ll a,ll p)
{
ll res = 1;
while(p)
{
if(p&1) res = res*a%M; //如果为奇数会走两次这里,1.p刚开始为奇数的时候。2.p == 1 的时候
a = a*a%M;
p >>= 1; //同 int 型的出发一样,3/2 == 1 。这里其实就是看二进制的最后一位是否为 1 来判断奇偶性,不管是不是奇都一分为二
}
return res;
}
typedef long long ll;
ll ksm(ll a,ll p){ll res = 1;while(p){if(p&1){res = res*a%M;}p >>= 1;a = a*a%M;}return res;}