快速幂
在求a的b次方时,暴力的做法是O(b)的,而有一种高效的O(log(b))的做法———快速幂
1 、快速幂的思想
因为a2b =(a2)b,底数增加了一倍,而时间复杂度却降低了一半。快速幂就是基于这个性质的一个算法。采用分治与倍增的思想。
假设我们要求ab,那么其实b是可以拆成二进制的,该二进制数第i位的权为2(i-1),例如当b=11时,a11=a(20+21+23)
11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算 a(20)*a(21)*a(23) ,看出来快的多了吧,原来算11次,现在算三次,但是这三项貌似不好求的样子…不急,下面会有详细解释。
由于是二进制,很自然地想到用位运算这个强大的工具: & 和 >> 。
(1) &运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶:x&1=0,则x为偶,x&1=1则x为奇。
(2)>>运算比较单纯,相当于除以2n。
快速幂的实现
1.定义函数f(a,b)=a的b次方
2.当b=1,f(a,b)=a
3.当b是偶数时,f(a,b)=f(a * a,b/2)
4.当b是奇数时,f(a,b)=f(a*a,b/2)*a
递归或迭代求解即可,时间复杂度O(log(b))
递归版代码
LL Quick( LL a, LL b) {
if( b == 1 ) return a;
LL ans = Quick(a, b>>1);
if( b&1 ) return ans * ans * a;
else return ans * ans; }
迭代版代码:
LL quick( LL a, LL pow ) {
LL ans = 1;
while ( pow > 0 )
{ if( pow&1 ) ans = ans * a;
a = a * a;
pow >>= 1;
}
return ans; }
3 例题:A的B次方
给定 n 组 ai,bi,pi,对于每组数据,求出 aibimod pi 的值。
输入格式 第一行包含整数 n。
接下来 n 行,每行包含三个整数 ai,bi,pi。
输出格式 对于每组数据,输出一个结果,表示 aibimod pi 的值。
每个结果占一行。
数据范围 1≤n≤100000, 1≤ai,bi,pi≤2×109
输入样例:
2
3 2 5
4 3 9
输出样例:
4
1
思路:裸的快速幂,中间取模即可
代码:
迭代版
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
while(n--)
{
long long a,b,p,res=1;
cin>>a>>b>>p;
while(b)
{
if(b&1) res=res*a%p;
b>>=1;//b右移了一位后,a也需要更新
a=a*a%p;
}
cout<<res<<endl;
}
}
递归版
#include<iostream>
using namespace std;
#define ull unsigned long long
ull quick_pow(ull a,ull b,ull p)
{
if(b==0) return 1;
a%=p;
ull res=quick_pow(a,b>>1,p);
if(b&1) return res*res%p*a%p;
return res*res%p;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b,p;
cin.tie(0);
ios::sync_with_stdio(false);
cin>>a>>b>>p;
cout<<quick_pow(a,b,p)<<endl;
}
return 0;
}
练习题目:
1、练习一
2、NOIP2013 转圈游戏
逆元
ax mod b=1的解x ,被称为模 b下 a的逆元,同时 a也是 x的逆元。逆元是个很有用的东西,在模运算中,a 的逆元x ,在运算中可以当多1/a 来用于计算。
“翠花,上酸菜”
1256 乘法逆元