[位运算加速/分治] 快速幂

快速幂的递归与非递归实现

  在众多题目中我们常常要计算amod 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; 
} 

[相关例题]

  P3197 [HNOI2008]越狱(洛谷)

[参考文献]

  《算法竞赛 进阶指南》-李煜东

posted @ 2018-06-07 12:36  FJ-Frank  阅读(591)  评论(0)    收藏  举报