这个文章的主要内容:倍增思想,快速幂算法题目;
其实这个主要是和高精度有点联系,算是这个高精度的进阶的题目,因此在学习这个高精度的时候老师说让啃一下这种比较难的题目,所以才有了现在的这个情况;
1.快速幂
这个在我们的后面需要学习的数学章节会经常使用到,因此这个快速幂的方法还是非常的实用的;
为什么叫做快速幂,这个很容易理解,因为这类题目涉及到的就是幂运算,为什么叫做快速呢,因为这个里面我们使用了倍增的思想,因此这个比正常的方法都是快的;
下面的这个是快速幂的代码:
1)和我们的高精度相比,这个代码真的是非常短了,主要还是理解这个倍增的过程把;
2)首先还是从这个main函数入手,输入三个数据,两个乘数,一个是取模运算的数据,按照这个指定的格式进行输出,因此这个就是使用的printf进行指定的格式的输出的;
3)调用这个函数的时候,这个就是我们的快速幂的核心了;
实际上我们的这个快速幂是对于这个指数,也就是我们的a的b次方运算里面的这个b进行二进制表示,二进制里面只有这个0,1,01之类的,是1的时候,我们就需要进行这个
#include <iostream>
using namespace std;
typedef long long LL;
LL a, b, p;
// 快速幂的模板
LL qpow(LL a, LL b, LL p)
{
LL ret = 1;
while(b)
{
if(b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
int main()
{
cin >> a >> b >> p;
printf("%lld^%lld mod %lld=%lld",a, b, p, qpow(a, b, p));
return 0;
}
4)下面的这个是老师上课时候的这个板书的具体的内容:使用的是11进行举例的,也就是把这个11进行二进制表示之后就是1011,这个也就表示成为我们的a的8次方乘以平方,乘以1次方;
这个8,2,1具体是如何得到的呢,就是我们的二进制得到的,也就是2的对应下标次方即可得到;
5)把这个思路应用到我们的代码里面去,那就是下面的这个函数:
ret存放的是我们的这个结果,b>>=1这个就是每一次都需要对于我们的b进行右移的操作,比如上面的这个1011的二进制表示,我们每一次右移,就可以达到对于这个二进制里面的每一位进行处理的这个效果;
6)b&1就是确定这个当前处理这个位是不是1,如果是1的话,这个时候就是真,进入我们的判断语句的内容,ret*a也就是每一次进行这个乘的过程,多少次呢,就取决于我们的这个b的大小,因为这个是while循环;
a=a*a就是我们的倍增的过程,从a的平方,到4次方,到8次方,到16次方这样倍增的过程
7)最后就是这个%p的操作,这个是我们的取模运算的特性,当这个式子里面只有这个取模运算和这个乘法运算的时候,我们的这个取模运算放在任何的这个地方都是没有问题的,因此这个地方为了解决这个存不下的问题,多次使用了这个取模的运算;
2.64位整数乘法
这个实际上就是食欲这个高精度乘法的这个范畴,但是这个里面使用了我们的倍增的思想,所以放到了这个位置
下面的这个是题目:
下面的这个是代码:
1)都是这个倍增的思想,这个快速幂运算的那个函数是完全一样的思想,不同的地方就是上面的是快速的乘法,下面的这个是快速的加法,其他的没有任何的区别,大家可以仔细的比对一下,这个思路也是完全一致的;
2)主函数里面也就那么一点东西,因此这个我就不在进行这个赘述了;
#include <iostream>
using namespace std;
typedef long long LL;
LL a, b, p;
// 快速乘的模板
LL qmul(LL a, LL b, LL p)
{
LL sum = 0;
while(b)
{
if(b & 1) sum = (sum + a) % p;
a = (a + a) % p; // 倍增
b >>= 1;
}
return sum;
}
int main()
{
cin >> a >> b >> p;
cout << qmul(a, b, p) << endl;
return 0;
}