快速幂求a的b次方%m

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

前言

 

当我知道快速幂之后。才发现 a ^ b还能这样算。数学之美就是你在能不断刷新你的认知。

 

 

 

快速幂的递归写法

我们已知 23  求 26,不就是 23 * 23嘛。快速幂的递归写法就是这个原理。

那有同学问了遇到奇数怎么办?25??

那不就是 2 * 24 这不就成了嘛。

所以这就是快速幂的基本思路求ab 

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

 

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

 

举个例子?210

210  =  25 * 25
25  =  2 * 24
24 = 22 * 22
22 = 21 * 21
21 = 2 * 20

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

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

long long quick_pow(long long a, long long b, long long m){//求a的b次方 
    if(b == 0){
        return 1;
    }else if(b % 2 == 1){
        return ((a % m) * (quick_pow(a, b-1, m) % m)) % m;
    }else{
        return ((quick_pow(a, b/2, m) % m) * (quick_pow(a, b/2, m) % m)) % m;
    }
}

int main(){ 
    long long a, b, c, ans = 1, cur = a;
    cin >> a >> b >> c;
    cout << quick_pow(a, b, c);
    return 0;
}

 

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

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

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

 

 

快速幂的迭代写法

 

a的b次方中a是底数平方这样上去的(2 4 16 256……),b是幂乘二*2上去的(2、4、8、16、32……)

x(2^29) …… x4 x2 x1


可以把109这么多次循环相乘转换成二进制的29位数字。109次方是怎么变成29位的二进制数呢?

1、log以2为底的109

2、9个log以2为底的10

3、9个log以2为底的10 -> 10是2的3.3次方

4、9*3.3大约等于29

5、29位

现在我们已经知道有多少位了,接下来可以通过前缀积的方式求出二进制每个位置上x是几次方。

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

通过同样的推导,我们可以把任意的a^b 表示成 a2^k……、a8、a4、a2、a1中若干的乘积。若果二进制的i号位为1.那么想中的a(2^i)就被选中。于是可以得到计算a^b的大致思路:令i 从0到k枚举b的二进制的每一位,如果为1 那就累计a(2^i)。注意a(2^k)……、a8、a4、a2、a1前一项总是等于后一项的平方。具体步骤如下。

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

(2)判断b的二进制末尾是否为1 ,(及判断 b&1 是否为 1)如果是的话,ans乘上a的值

(3)a赋值为a平方,每往左边移动一位就要求一次平方。这样如果二进制位上是1的话就可以直接乘上a了。

(4)b=b/2,表示要求下一个位置的值是不是1了

(5)如果b<0了返回ans的值。

 例:a13

b b&1 ans a
    1 a
1101 1 1*a=a a2
110 0 a a4
11 1 a*a4 = a5 a8
1 1 a5 * a8 = a13  

迭代代码:

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

long long quick_pow(long long a, long long b, long long m){
    long long ans = 1; 
    while(b > 0){
        if(b & 1){
            ans = ans * a % m;
        }
        a = a * a % m;//每一个位置都要乘一次平方,底数平方
        b >>= 1;//判断下一个位置了,幂乘二
    }
    return ans;//返回累积的结果 
}

int main(){
    int a,b,c;
    cin >> a >> b >> c;
    cout << quick_pow(a,b,c);
    return 0;
}
posted @ 2020-04-28 12:03  elisa02  阅读(658)  评论(0编辑  收藏  举报