快速幂的目的就是做到快速求幂
假设我们要求ab,按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn),快了好多好多。它的原理如下:
假设我们要求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¹¹转化为算 a20*a21*a23,也就是a1*a2*a8 ,看出来快的多了吧原来算11次
由于是二进制,很自然地想到用位运算这个强大的工具:&和>> &运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶x&1==0为偶,x&1 ==1为奇。 >>运算比较单纯,二进制去掉最后一位,不多说了,先放代码再解释。
经典例题
AcWing 875. 快速幂
给定n组ai,bi,pi,对于每组数据,求出aibi mod pi的值。
输入格式
第一行包含整数n。
接下来n行,每行包含三个整数ai,bi,pi。
输出格式
对于每组数据,输出一个结果,表示aibi mod pi的值。
每个结果占一行。
数据范围
1≤n≤100000,
1≤ai,bi,pi≤2∗109
输入样例:
2
3 2 5
4 3 9
输出样例:
4
1
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while (b)
{
if (b & 1) res = res * a % p;//两数相乘可能会爆Int
a = a * (LL)a % p;// 计算后, a平方
b >>= 1;//移去最低位
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
printf("%lld\n", qmi(a, b, p));
}
return 0;
}
AcWing 876. 快速幂求逆元
给定n组ai,pi,其中pi是质数,求ai模pi的乘法逆元,若逆元不存在则输出impossible。
注意:请返回在0∼p−1之间的逆元。
乘法逆元的定义
若整数b,m互质,并且对于任意的整数 a,如果满足b|a,则存在一个整数x,使得a/b≡a∗x(mod m),则称x为b的模m乘法逆元,记为b−1(mod m)。
b存在乘法逆元的充要条件是b与模数m互质。当模数m为质数时,bm−2即为b的乘法逆元。
输入格式
第一行包含整数n。
接下来n行,每行包含一个数组ai,pi,数据保证pi是质数。
输出格式
输出共n行,每组数据输出一个结果,每个结果占一行。
若ai模pi的乘法逆元存在,则输出一个整数,表示逆元,否则输出impossible。
数据范围
1≤n≤105,
1≤ai,pi≤2∗109
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible
当n为质数时,可以用快速幂求逆元:
a / b ≡ a * x (mod n)
两边同乘b可得 a ≡ a * b * x (mod n)
即 1 ≡ b * x (mod n)
同 b * x ≡ 1 (mod n)
由费马小定理可知,当n为质数时
b ^ (n - 1) ≡ 1 (mod n)
拆一个b出来可得 b * b ^ (n - 2) ≡ 1 (mod n)
故当n为质数时,b的乘法逆元 x = b ^ (n - 2)
当n不是质数时,可以用扩展欧几里得算法求逆元:
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1
exgcd(a, p, x, y)
作者:Hz
链接:https://www.acwing.com/solution/content/3054/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
快速幂求逆元
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while(b){
if(b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n; cin >> n;
while(n --){
int a, p;
cin >> a >> p;
if(a % p == 0) puts("impossible");
else cout << qmi(a, p - 2, p) << endl;
}
return 0;
}
作者:Hz
链接:https://www.acwing.com/solution/content/3054/
来源:AcWing
扩展欧几里得算法求逆元
#include <iostream>
using namespace std;
typedef long long LL;
int n;
int exgcd(int a, int b, int &x, int &y)
{
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
cin >> n;
while (n --)
{
int a, p, x, y;
// if (a < p) swap(a, p);
cin >> a >> p;
int d = exgcd(a, p, x, y);
if (d == 1) cout << ((LL)x + p) % p << endl;//保证x是正数
else puts("impossible");
}
return 0;
}
作者:Hz
链接:https://www.acwing.com/solution/content/3054/
来源:AcWing
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)//快速幂
{
LL res = 1;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, p;
scanf("%d%d", &a, &p);//p是质数 求 a的逆元(mod p意义下)
if (a % p == 0) puts("impossible");
else printf("%lld\n", qmi(a, p - 2, p));
}
return 0;
}