当前位置:首页 > 算法 > 正文内容

【分治】----快速幂

亿万年的星光4年前 (2021-01-28)算法23483
1.幂

幂(power)是指乘方运算的结果。n^m指该式意义为m个n相乘。把n^m看作乘方的结果,叫做n的m次幂,也叫n的m次方。

2.幂的数学表示和规则

2^3  *   2^4  = 2^7


3^4  *  3^4  = 3^8


3.分治法求快速幂

在平常中我们如果遇到求 a^b的时候,可能是一个一个挨个乘起来,也就是将a乘b次,如果b的值非常大时,在算法竞赛中又有时间限制,累次乘几乎是不行的。(不要说这个时候用pow() 函数)例如:21000,用循环累次乘的话需要1000次。

现在我们如果要求2^11的值,那么就要计算 22222222222

根据前面幂的运算法则,可以得出2^11=2^5 * 2^5 * 2^1    。  2^5=2^2 * 2^2 *2^1 。 2^2=2^1 * 2^1

再例如:现在我们如果要求 216的值,那么就要计算 16个2相乘。根据前面幂的运算法则,可以得出

2^16=2^8 * 2^8  。 2^8=2^4 * 2^4  。 2^4=2^2 * 2^2  。  2^2=2^1 * 2^1

从上面两个例子可以看出,如果用类似于次方一半的方式进行累乘,那么需要计算的次数可以减少很多。这就是分治法求快速幂的核心。

再来找一下分治法求快速幂的规律。可以看出上面两个的主要区别在于次方是奇数还是偶数,如果是次方b是偶数,只需要每次乘ab/2
 ,然后将次方b缩减一半即可。如果次方b是个奇数,可以先将这个a自乘一次,这样剩余的b-1次方就是偶数了。

快速幂是一个将底数等价增大,指数等价缩小的过程。总结:b是偶数:image.png b是奇数:

image.png  我们用res表示结果;那么求ab的过程代码描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int a,b,res=1; //定义底数,次数,结果
    cin>>a>>b;
    while(b!=0) //次方减半不到0时继续
    {
        if(b%2==1)  //如果b是奇数,(最后一次一定是奇数)
            res=res*a;  //  先乘自己一次,这样就能一半是偶数
        a=a*a;  // a^2 ,a^4, a^8 ...
        b=b/2; // 这个地方如果是b>>=1 速度更快
    }  
    cout<<res<<endl;
    return 0;
}

image.png



image.png


311的动图演示。


4.递归法求快速幂

#include<cstdio>
int pow_mod(int a ,int n) {
   if(n == 0)
       return 1;
   int x = pow_mod(a,n / 2);
   long long ans = (long long)x * x ;
   if(n%2 == 1)
       ans = ans *a ;
   return (int)ans;
}
int main() {
   int a,b;
   scanf("%d%d",&a,&b);
   printf("%d",pow_mod(a,b));
   return 0;
}

5.迭代法求快速幂
#include<bits/stdc++.h>
using namespace std;
long long pow_d(long  a,long  b){
   long ans=1;
   while(b>0){
       if(b&1){//如果b的二进制末尾为1 ,就相当于被选中了。
      //就如2^13 ==> 2^(13==>1101)==> 2^(1101) ==> 3 2 0 号为 1 那么被选中 ==> 2^13 = 2^8 *  2^4 * 2^1
           ans=ans*a;//令ans累积上a
       }
       a=a*a;//令a平方
       b>>=1;//将b的二进制右移一位即
   }
   return ans;
}
int main(){
   long long  a,b;
   cin>>a>>b;
   long long  result=pow_d(a,b);
   cout<<result<<endl;
   return 0;
}

6.快速幂中的取余运算

搜索快速幂的时候经常发现最后有个取余运算,主要原因是因为现在oj网站的题或者竞赛的题,如果a的b次幂且b很大,那么题中大多会让你把结果对一个数取余也就是求模,例如a^b%c这种。

介绍一个公式:
(a*b)%m = (a%m * b%m) %m

所以在一些求快速幂中会出现取余现象。

例题

【题目描述】

假设今天是星期日,那么过 a^b天之后是星期几?
【输入描述】

两个正整数 a,b,中间用单个空格隔开。100000<a≤100,0<b≤10000。
【输出描述】

一个字符串,代表过 a^b 天之后是星期几。
其中,Monday 是星期一,Tuesday 是星期二,Wednesday 是星期三,Thursday 是星期四,Friday 是星期五,Saturday 是星期六,Sunday 是星期日。
【样例输入】

3 2000
【样例输出】

Tuesday

【分析】上面这道题目出现了2000次方,比较大所以考虑到取余的问题,实际上我们只有按照题目要求,每次运算求幂的时候对7取余即可。类似下面这样:

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int a,b,res=1,m=7; //定义底数,次数,结果,取余
    cin>>a>>b;
    while(b!=0) 
    {
        if(b%2==1)  //如果b是奇数,(最后一次一定是奇数)
            res=res*a%m;  
        a=a*a%m;   
        b=b/2; 
    }   
    cout<<res<<endl;
    return 0; 
}

扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

标签: 快速幂
分享给朋友:

相关文章

【算法】前缀和与差分(2)一 一维数组差分

【算法】前缀和与差分(2)一 一维数组差分

一、差分:一维数组的差分可看作是一维数组前缀和的逆运算。二、差分数组首先给定一个原数组a:   a[1]、a[2]、a[3]、......然后构造一个数组b: b[1]、b[2]、...

【贪心】区间调度

【贪心】区间调度

【题目描述】有n项工作,每项工作分别在si时间开始,在ti时间结束。对于每项工作,你都可以选择参与与否。如果选择了参与,那么自始至终都必须全程参与。此外,参与工作的时间段不能重叠(即使是开始与结束的瞬...

【贪心】 导弹拦截

【贪心】 导弹拦截

【题目描述】某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来...

【算法】最短路径算法——Floyed-Warshell算法

【算法】最短路径算法——Floyed-Warshell算法

如下图所示,我们把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。一张图中任意两点间会有不同的路径相怜。最短路径就是指连接两点的这些路径中最短的一条。【注意】边的权值可以为负。当出现负边权...

【贪心】最大子矩阵

【题目描述】已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1X1)子矩阵。比如,如下4 x 4的矩阵0    -2&...

【算法】动态规划(二)——数字三角形问题

【算法】动态规划(二)——数字三角形问题

1.问题描述及状态定义数字三角形问题:有一个非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数字的坐下方和右下方各有一个数。如下图所示:从第一行开的数开始走,每次可以往下或右走一格,直到走到...