快速幂概念
幂运算 an 即 n 个 a 相乘。快速幂就是高效地算出 an 。
题目
洛谷 : P1226 【模板】快速幂||取余运算
快速幂算法实现
当 n 很大时,如 n = 109 ,计算时间会很长,且结果会很大,常常超过变量类型的最大值。下面先考虑如何缩短计算时间
解决结果过大超过变量范围的问题需要用到高精度
暴力思想
遇见这个问题,我相信你们的第一意识便是直接循环n次,让它乘就完了。逐个做乘法计算 an ,即计算 a×a×a×……a。
#include <bits/stdc++.h>
using namespace std;
#define int long long
//计算 a^n mod m 的朴素算法
int Pow(int a,int n,int m){
int res = 1;
while(n--)
res = (res*a)%m;//关于取模会在后面介绍,这里先做个引子
return res%m;
}
//将long long 重命名为int ,这里便不能用int main,好在c++提供了signed代替原int功能
signed main(){
int b,p,k;
cin>>b>>p>>k;
printf("%lld^%lld mod %lld=%lld",b,p,k,Pow(b,p,k));
return 0;
}
这样的代码非常容易理解,也非常容易实现,但是,尽管这样可以计算a的n次幂,但是计算的时间太长,往往会超时,蛤,这都能超时 , 你看
暴力法时间复杂度为 O(n),所以几乎是必超时的。
分治思想
对于 an 我们可以先算 a2 , 然后再计算 (a2)2 ,依次类推。也就是
当n为偶数时 , 将 an 分解为 an/2 × an/2 , 再将 an/2 分解为 an/4 ……
当n为奇数时 , 将 an 分解为 a(n-1)/2+1 × a(n-1)/2 , 然后依次类推 ……
这就是分治思想,将大问题分为几个小问题,算出小问题的答案,然后得出最终答案
//计算a^n
int qpow(int a,int n){
if(n == 0) return 1;
if(n == 1) return a;
int temp = qpow(a,n/2);
if(n%2 == 1)
return temp * temp * a;
else
return temp * temp;
}
时间复杂度为 O(log2n),分治的代码还是感觉有点长,而且判断有点多。有没有更加容易理解而且更短的实现呢!
位运算思想
以a11 为例来说明位运算,a11 = a8 ×a2 ×a1 , 不难看出2,8…都是2的倍数即a1 ×a1=a2
,a2 × a2 = a4 ,a4 × a4 = a8 ,产生的都是倍乘关系,逐级递推就可以。如何将11分解为8+2+1,这便利用了二进制,11的二进制为1011,即23+21+20, 也即8+2+1,这样就完成了分解。
下面的代码便是完成这样的操作:
int qpow(int a,int n){
int res = 1;
while(n){
if(n&1) res = res*a;//n&1 表示检测当前n的二进制的最后一位是不是1,如果是1就该开始乘了
a = a*a;//无论是不是1,a都要乘一次
n>>=1;//等价于n/=2,不过这样更快。
}
return res;
}
时间复杂度为 O(log2n),ok,我们发现位运算的快速幂代码很短,而且很好 背出来 写出来。并且这玩意长得很像朴素算法。。仅仅比朴素算法多了两行,带来的却是质的提升!
快速幂取模
由于幂运算的结果非常大,常常会超过变量类型的最大值,甚至超过内存所能存放的最大数,所以题目往往会要求对结果进行取模操作,缩小结果。
模运算公式
加 (a+b) mod m = ( (a mod m) + (b mod m) ) mod m
减 (a-b) mod m = ( (a mod m) - (b mod m) ) mod m
乘 (a×b) mod m = ( (a mod m) × (b mod m) ) mod m
然而,对除法进行类似 (a/b) mod m = ( (a mod m) / (b mod m) ) mod m 的操作是错误的,如(100/50) mod 20 = 2 , 而( (100 mod 20) / (50 mod 20) ) mod 20 = 0,两者并不相等,除法的取模需要用到逆元
显然由乘法的模运算公式可以推出 an mod m = (a mod m)n mod m
下面加上取模的算法都是依靠这个公式进行的
分治思想算法取模
int qpow(int a,int n,int m){
if(n == 0) return 1%m;
if(n == 1) return a%m;
int temp = qpow(a,n/2,m);
if(n%2 == 1)
return (temp * temp * a) %m;
else
return (temp * temp) %m;
}
位运算思想算法取模
int qpow(int a,int n,int m){
int res = 1;
while(n){
if(n&1) res = (res*a)%m;
a = (a*a)%m;
n>>=1;
}
return res%m;
}
题目代码
有了取模的快速幂,我们就能很愉快的A了这道例题!
#include <bits/stdc++.h>
using namespace std;
#define int long long
int qpow(int a,int n,int m){
int res = 1;
while(n){
if(n&1) res = (res*a)%m;
a = (a*a)%m;
n>>=1;
}
return res%m;
}
signed main(){
int b,p,k;
cin>>b>>p>>k;
printf("%lld^%lld mod %lld=%lld",b,p,k,qpow(b,p,k));
return 0;
}
- 所以今后遇到要求 a的n次方 的问题,顺手写个快速幂总没错的,毕竟,它这么短。