快速幂
1.基础的快速幂
对于快速幂问题 之前的解决方法是逐步合并 ,比如 4 5 4^5 45我们可以先变成 1 6 2 ∗ 4 16^2*4 162∗4,然后再变成 256 ∗ 4 256*4 256∗4,但是这样有一个缺点就是代码很容易写错,然后学习到了一种新的求解快速幂的方法。
首先为什么要引进快速幂,如果我们要求 4 n 4^n 4n,那么我们暴力的做法就是写个for循环 但是 这样的时间复杂度是 O ( n ) O(n) O(n)的 时间复杂度太高了 ,因此,我们要引进快速幂 。
首先,整一些先导知识,第一,就是二进制,我们知道,对于任意一个10进制的数,都能用二进制来表示(不然计算机就无法运行了),那么接下来就简单了 可以看以下公式
4
5
=
4
2
1
∗
4
2
2
(
1
)
4^5=4^{2^1}*4^{2^2} (1)
45=421∗422(1)
也就是说,对于求幂运算的指数,我们可以把它转化为2进制去进行计算,也就是说,而且,我们可以观察到
4
2
1
4^{2^1}
421到
4
2
2
4^{2^2}
422,我们只需要把
4
2
1
4^{2^1}
421 平方即可,这样我们就可以求快速幂了 。下面直接进入coding可能好理解一点。
coding:
#include<iostream>
using namespace std;
int n;
typedef long long int LL;
LL qmi(int a,int b,int p)
{
// 求解 a^b%p的结果
LL res=1;
int k=b;
while(k!=0)
{
// 如果k这个数二进制表示最后一位是1 也就是这一位
if(k&1)
{
res=res*a%p;
}
// 左移一位看看下一位是不是1
k=k>>1;
a=a*(LL)a%p;
// cout<<k<<endl;
}
return res;
}
int main()
{
cin>>n;
while(n--)
{
int a,b,p;
cin>>a>>b>>p;
cout<<qmi(a,b,p)<<endl;
}
return 0;
}
注意,在代码当中 我们是按照公式(1)把我们的结果拆成了 4 2 k 4^{2^k} 42k 相乘 也就是把系数b转化为了2进制
注意,我们的求解ans和不断平方a以得到a的更高次方是一个同时的过程
而这个代码的时间复杂度是 ( l o g 2 n ) (log_2n) (log2n)的 是可以接受的 。
2.快速幂求解逆元
什么是逆元 概念很抽象 这里可以这么理解
a
÷
b
≡
a
∗
x
(
m
o
d
m
)
(
2
)
a\div b \equiv a*x(\ mod\ m) (2)
a÷b≡a∗x( mod m)(2)
就是如果b|a 那么 a ÷ b a\div b a÷b就一定是一个整数(废话),但是,对于除法,我们感觉是很麻烦的 因此 我们是不是转换为乘法那,于是,我们就想到了逆元 如果满足公式(2) 那么我们就把x叫做b的模m乘法逆元 其中b与m必须是互质的
我们可以把公式(2)改写一下 令x等于 b − 1 b^{-1} b−1
方程两边乘b
a
≡
a
∗
b
−
1
∗
b
(
m
o
d
p
)
a \equiv a*b^{-1}*b(mod\ p)
a≡a∗b−1∗b(mod p)
消除两边的a
b
∗
b
−
1
≡
1
(
m
o
d
p
)
b*b^{-1}\equiv 1(mod\ p)
b∗b−1≡1(mod p)
然后根据费马小引理: 如果p是一个质数 a不是p的倍数 也就是a和p互质 那么
a
p
−
1
=
1
(
m
o
d
p
)
a^{p-1}=1(mod\ p)
ap−1=1(mod p)
那么我们最后的式子可以改写为 b ∗ b p − 2 = 1 ( m o d p ) b*b^{p-2}=1(mod\ p) b∗bp−2=1(mod p)
那么我们就可以推出来 逆元为 b p − 2 b^{p-2} bp−2方了 那么求逆元的情况我们就可以 变成快速幂的问题了 注意 只有当p为质数的时候才成立 不然不符合费马小引理
如果b和p不互质 那么就不存在逆元
接下来我们可以举一个简单的例子说明一下逆元 比如 我们的 b是4 p是3 我们给定一个b|a的数也就是a等于8 我们要把8/4 mod p等价成一个乘法 我们就可以去求 b p − 2 b^{p-2} bp−2 也就是 4 然后这个4就是我们要求的逆元 我们会发现 4*8 mod 3 余2 和除法的结果相同 也就是说 这个4就是我们要求的逆元
coding如下:
#include<iostream>
using namespace std;
int n;
typedef long long int LL;
LL qmi(int a,int b,int p)
{
// 求解 a^b%p的结果
LL res=1;
int k=b;
while(k!=0)
{
// 如果k这个数二进制表示最后一位是1 也就是这一位
if(k&1)
{
res=res*a%p;
}
// 左移一位看看下一位是不是1
k=k>>1;
a=a*(LL)a%p;
}
// cout<<"balabala"<<a<<endl;
return res;
}
int main()
{
cin>>n;
while(n--)
{
int a,p;
cin>>a>>p;
if(a%p==0)
{
cout<<"impossible"<<endl;
continue;
}
cout<<qmi(a,p-2,p)<<endl;
}
return 0;
}