同余和快速幂
这一块一直比较懵逼,所以写一篇分享记录一下。
同余
先介绍同余吧。
同余:对于两个整数a和b,若它们除以整数m所得得余数相等,就称a与b对于模m同余或者a同余于b模m。记作a≡b(modm)。
同余的性质
1. 反身性。 a≡a(modm)
2. 对称性。 若a≡b(modm),则b≡a(modm)
3. 传递性。 若a≡b(modm),b≡c(modm),则a≡c(modm)
4. 同余式相加。若a≡b(modm),c≡d(modm),则a±c≡b±d(modm)
5. 同余式相乘。若a≡b(modm),c≡d(modm),则ac≡bd(modm)
6. 幂运算。若a≡b(modm),则有an≡bn(modm)
同余性质证明
反身性、对称性以及传递性均可由定义得证。
同余式相加的证明
设a%m=b%m=p,c%m=d%m=q,
则有a=⌊a/m⌋m+p,b=⌊b/m⌋m+p,c=⌊c/m⌋m+q,d=⌊d/m⌋m+q。
那么a±c=⌊a/m⌋m+p±(⌊c/m⌋m+q)=(⌊a/m±c/m⌋)+(p±q),
同理可得b±d=(⌊b/m±d/m⌋)+(p±q)
因为(a±c)%m=(b±d)%m=p±q,所以a±c≡b±d(modm)。
同余式相乘的证明
设a%m=b%m=p,c%m=d%m=q,
则有a=⌊a/m⌋m+p,b=⌊b/m⌋m+p,c=⌊c/m⌋m+q,d=⌊d/m⌋m+q。
那么a×c=(⌊a/m⌋m+p)×(⌊c/m⌋m+q)=⌊a/m⌋⌊c/m⌋m2+(⌊a/m⌋q+⌊c/m⌋p)m+pq,
同理可得b×d=(⌊b/m⌋m+p)×(⌊d/m⌋m+q)=⌊b/m⌋⌊d/m⌋m2+(⌊b/m⌋q+⌊d/m⌋p)m+pq。
因为(a×b)%m=pq=(b×d)%m,所以a×c≡b×d(modm)
我们注意到(a%p)×(c%p)=pq,那么有(a%p)×(c%p)=(a×c)%p
幂运算的证明
可以由同余式相乘推出。
具体而言令c=a,d=b,即可得到a2≡b2(modm),
利用上一步得到的结果,再令c=a2,d=b2,即可得到a3≡b3(modm),
⋮
以此类推,便可以证明an≡bn(modm)。
同余相关定理(或者说同余的应用)
- 欧拉定理
- 费马小定理,用于求解乘法逆元
- 中国剩余定理(也称孙子定理)
链接如下:https://www.acwing.com/blog/content/22048/
快速幂
问题:给定一组数据a,b,p,求出abmodp的值
乍一看,很简单啊,写个循环不就完事了嘛!
long long res = 1;
for(int i = 0;i < b; ++ i){
res = res * a;
}
res = res % p;
以上代码有两个问题,第一个问题是a可能达到2×109,那么a×a是会爆int甚至long long类型的,第二个问题是如果给定n组数据,而且n=100000,那么上述解法的时间复杂度就会很高,最后导致TLE。
解决办法
针对第一个问题,我们利用上面介绍的同余性质来解决。
具体而言,abmodp=(a×ab−1)modp=(amodp)(ab−1modp),这个等式在前面已经证明过了。因此,我们可以在写代码的过程中随时模p。
针对第二个问题,我们利用二进制优化该算法。
具体而言,就是将b用二进制数来表示,即b=0001⋯100,进一步表示为2的指数形式,即b=2n+⋯+22,那么ab=a2n×⋯×a22,因此我们只需要有限个求解a的幂次即可,该幂次对应于b的二进制表示中不等于0对应的位数。
快速幂算法
需要预处理a20,a21,⋯,a2n,其中,a21=a20×a20,⋯,a2n=a2n−1×a2n−1,因此快速幂算法又称为平方求幂算法。
上述预处理操作需要在b完成位运算(&)后进行。
具体代码如下:
long long quick_power(int a,int b,int p){
long long res = 1;
while(b){
if(b & 1) res = res * a % p;//当前位为1
a = (long long)a * a % p;
b >>= 1;
}
return res;
}
当然了,还有其他求解快速幂的算法,比如利用递归+二分来求解,这里就不作介绍了。