快速幂求a的b次方%m

Posted elisa02

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速幂求a的b次方%m相关的知识,希望对你有一定的参考价值。

题目网址:http://class.51nod.com/Challenge/Problem.html#problemId=1046

快速幂的递归写法

当我知道快速幂之后。才发现 a ^ b还能这样算,太秀了吧,当然你必须带一些二分和递归,不然你看不懂它的递归式。会想(这TM是What ??? )数学之美就是你在能不断刷新你的认知,还TM能这样,太秀了吧,然后你一跺脚,一拍手,就学会了。神奇的快速幂,时间复杂度O(logb).

我们已知 2^3  求 2^6,不就是 2^3 * 2^3嘛。快速幂就是这个原理。

 

那有同学问了遇到奇数怎么办?2 ^ 5??

 

那不就是 2 * 2 ^ 4 这不就成了嘛。

 

所以这就是快速幂的基本思路求a ^ b

 

1)当b是奇数时,那么有 a^b = a * a^*(b-1)

 

2)当b是偶数时,那么有 a^b = a^(b/2) * a^(b/2)

 

举个例子?2 ^10

2^10  =  2^5 * 2^5
2^5  =  2 * 2^4
2^4 = 2^2 * 2^2
2^2 = 2^1 * 2^1
2^1 = 2 * 2^0


根据这两个条件写递归式嘛,这还不简单?

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 long long quick_pow(int a, int b){
 6     if(b == 0){
 7         return 1;
 8     }else if(b & 1){
 9         return a*quick_pow(a, b-1);
10     }else{
11         return quick_pow(a, b/2) * quick_pow(a, b/2);
12     }
13 }
14 
15 int main(){ 
16     long long a, b, c, ans = 1, cur = a;
17     cin >> a >> b >> c;
18     cout << quick_pow(a, b) % c;
19     return 0;
20 }

针对不同的题目,有两个细节需要注意

1)如果初始值a 大于 m ,那么需要在进入函数前就让a 对 m 取模,

2)若果m 为 1,可以直接在函数外部特判为 0,不需要进入函数来计算。(因为任何数对1 取模都是0)

 

快速幂的迭代写法

对于 a ^ b来说,若果把 b 写成2 进制,那么b 就可以写成若干二次幂之和,如13 的二进制 1101,于是3 号位 、2号位、0号位就都是1,那么就可以得到13 = 2^3 + 2^2 + 2^1 = 8 + 4 + 1。所以a ^13 = a^8 * a^4 * a^1。

通过同样的推导,我们可以把任意的a^b 表示成 a^(2^k)……、a^8、a^4、a^2、a^1中若干的乘积。若果二进制的i号位为1.那么想中的a^(2^i)就被选中。于是可以得到计算a^b的大致思路:令i 从0到k枚举b的二进制的每一位,如果为1 那就累计a^(2^i)。注意

a^(2^k)……、a^8、a^4、a^2、a^1前一项总是等于后一项的平方。具体步骤。

(1)初始令ans = 1,用来存放累积的结果。

(2)判断b的二进制末尾是否为1 ,(及判断 b&1 是否为 1),也可以理解为判断b 是否为奇数。如果是的话,令ans乘上a的值。

(3)令a平方,并使b右移一位,(也可以理解为,b/2)

(4)只要b 大于0,就返回(2)。

 例:a^13

b b&1 ans a
    1 a
1101 1 1*a=a a^2
110 0 a a^4
11 1 a*a^4 = a^5 a^8
1 1 a^5 * a^8 = a ^ 13  

迭代代码:

#include<cstdio>
#include<iostream>
using namespace std;

long long quick_pow(long long a, long long b, long long m){
    long long ans = 1;//初始令ans = 1,用来存放累积的结果
    while(b > 0){
        if(b & 1){//判断b的二进制末尾是否为1 
            ans = ans * a % m;//如果是的话,令ans乘上a的值 
        }
        a = a * a % m;//令a平方 
        b >>= 1; //使b右移一位,(也可以理解为,b/2) 
    } 
    return ans;//返回累积的结果 
}

int main(){
    int a,b,c;
    cin >> a >> b >> c;
    cout << quick_pow(a,b,c);
}

总结

我认为递归更好记,迭代还可以,但是我们知道了 我们需要补习一下位运算。

咦,递归和2分好像啊。。。。递归如果以中间作为中点递归,感觉就是二分的一种。

 

以上是关于快速幂求a的b次方%m的主要内容,如果未能解决你的问题,请参考以下文章

GDUFE-OJ 1203 快速幂

幂求模

矩阵快速幂求斐波那契

乘法逆元(模板)

hdu_2837_Calculation(欧拉函数,快速幂求指数循环节) (待查

AcWing 876. 快速幂求逆元