[位运算加速/分治] 快速幂
快速幂的递归与非递归实现
在众多题目中我们常常要计算ab mod c的数值(例如求逆元ac-2 mod c),若我们逐次乘以a记入答案中,这效率显然是非常低的.那么我们有没有办法快速地实现幂运算呢?这里我们展示一种(二进制)位运算实现快速幂的方法.
[算法描述]
根据数学常识,每一个整数可以唯一地表示为若干指数不重复的2的次幂的和.也就是说,如果b在二进制下有k位,其中第i(0≤i<k)位的数字是ci(ci=1或0),那么
b=ck-12k-1+ck-22k-2+…+c020(即为b的二进制表达式)
于是:
ab=ack-1×2^k-1×ack-2×2^k-2×…×ac0×2^0
由幂运算可知:
a2n=an×an(即a2^i=a2^i-1×a2^i-1)
于是我们可以逐位取出第i位(b&1,如果第1位为0,b&1的值为0,如果第1位为1,b&1的值为1),每次取出1位后就可以舍去最低位(b>>=1,表示右移1位),我们递推处理需要乘的数,并乘入ans中mod c即可在O(log2b)的时间算出ab mod c的数值
[代码]
非递归代码如下:
/* Name: Quick_power Author: FZSZ-LinHua Date: 2018 06 07 Time complexity: O(log n) Algorithm: Bit operation */ # include "iostream" # include "cstdio" using namespace std; inline long long quick_power(long long x,long long y,long long z){ long long ans=1; //ans要初始化为1,否则乘以任何数的结果均是0 while(y){ //当y不为0时继续进行快速幂 if(y&1){ //如果这一位是1则乘入答案 ans=(ans*x)%z; } y>>=1; //舍弃最低位 x=(x*x)%z; //计算a^(2^i) } return ans; //返回答案 } int main(){ long long a,b,c; scanf("%lld%lld%lld",&a,&b,&c); printf("%lld",quick_pow(a,b,c)); return 0; }
递归(分治)实现:我们发现如果若b是偶数那么ab=ab/2×ab/2若b是奇数那么ab=a[b/2]×a[b/2]×a([x/2]表示x/2下取整),这样我们可以将ab分解成a[b/2]并求解(设置一个边界 b=0时返回1)
/* Name: Quick_power Author: FZSZ-LinHua Date: 2018 06 07 Time complexity: O(log n) Algorithm: Divide-and-conquer */ # include "iostream" # include "cstdio" using namespace std; long long quick_power(long long x,long long y,long long z){ if(y==0){ //设置边界 return 1; } long long ans =quick_pow(x,y>>1,z); //y>>=1等价于y/2下取整 ans=ans*ans%z; if(y&1){ //如果这是个奇数就要多乘上一个x ans=ans*x%z; } return ans; //返回答案 } int main(){ long long a,b,c; scanf("%lld%lld%lld",&a,&b,&c); printf("%lld",quick_power(a,b,c)); return 0; }
[相关例题]
[参考文献]
《算法竞赛 进阶指南》-李煜东

浙公网安备 33010602011771号