1.乘法逆元的定义及作用
定义(口胡):
乘法逆元是取模运算中的一个东西。假设 a x ≡ 1 ( m o d p ) ax≡1 (mod p) ax≡1(modp) 并且GCD(a,p)=1 (a,p互质),那么x就是a的逆元。对于给定的a和p,有且仅有一个数是它的逆元。
乘法逆元的作用:
因为取模这个运算是不满足“除法分配率”的,这就导致了如果a,b或者p很大的话, ( a b ) m o d p (\frac a b) \mod p (ba)modp 这个运算就会爆精度。那么我们就要用另外一种方法来计算这个式子,而乘法逆元给我们提供了一个很好的途径。
( a b ) m o d p = ( a b ) ∗ 1 m o d p = ( a b ) ∗ ( b ∗ x ) m o d p = a ∗ x m o d p ( x 为 b 的 逆 元 时 ) (\frac a b) \mod p = (\frac a b)*1 \mod p = (\frac a b)* (b*x) \mod p =a*x \mod p (x为b的逆元时) (ba)modp=(ba)∗1modp=(ba)∗(b∗x)modp=a∗xmodp(x为b的逆元时)
这样我们就把除法变成满足Mod分配率的乘法了。
##2.乘法逆元的求法
1.扩展欧几里德求逆元
什么是扩展欧几里德
此时a,p一定要满足互质
对于式子 ax≡1 (mod p) 我们可以将它变成一个方程: a ∗ x + p ∗ y = 1 a*x+p*y=1 a∗x+p∗y=1
由于我们前面定义中说明了 G C D ( a , p ) = 1 GCD(a,p)=1 GCD(a,p)=1,此时由于裴蜀定理,该方程一定至少有一组解。我们可以用扩展欧几里德算法求得特殊解,再套用通解公式: y = y 0 − a G C D ( a , b ) ∗ t y=y0-\frac a {GCD(a,b)}*t y=y0−GCD(a,b)a∗t求出满足mod p条件的一组解(也就是mod一下啦)
代码如下:
int exgcd(int a,int b,int &x,int &y){
if (b==0) {x=1,y=0; return x;}
int ret=exgcd(b,a%b,x,y);
int t=x; x=y,y=t-a/b*y;
return ret;
}
int calc(int n,int p){
int pd=exgcd(n,p,x,y);
if (pd==1) return (x%p+p)%p;
}
2.费马小定理求逆元
首先了解一下费马小定理:
p是素数时,对于任意整数x都有 x p ≡ x ( m o d p ) x^p ≡ x (mod p) xp≡x(modp)。
如果x为整数且x,p互质,则有 x p − 1 ≡ 1 ( m o d p ) x^{p−1} ≡ 1 (mod p) xp−1≡1(modp)。
证明详见百度百科:传送门
那么就可以来推导了: x p − 1 ≡ x ∗ x p − 2 ≡ 1 ( m o d p ) x^{p−1} ≡ x*x^{p-2} ≡ 1 (mod p) xp−1≡x∗xp−2≡1(modp)。那么根据乘法逆元的定义可以得到x的逆元就是 x p − 2 x^{p-2} xp−2。求 x p − 2 x^{p-2} xp−2只要用一下快速幂就好了。当然成立的条件是满足x,p互质。
代码如下:
int qsm(int x,int y){
int ans=1,w=x;
while (y) {
if (y%2==1) ans=(ans*w)%p;
y/=2; w=(w*w)%p;
}
return ans;
}
int calc(int n,int p){
return qsm(n,p-2);
}
3.欧拉函数求逆元
欧拉定理:对于两个互质的正整数a,p(p>2)有: x φ ( p ) ≡ 1 ( m o d p ) x^φ(p) ≡ 1(mod p) xφ(p)≡1(modp)。
变形一下就可以得到: x ∗ x ( φ ( p ) − 1 ) ≡ 1 ( m o d p ) x*x^{(φ(p)-1)} ≡ 1(mod p) x∗x(φ(p)−1)≡1(modp)
那么我们就可以先求出欧拉函数值φ§然后快速幂就能够解决了。
这种方法成立的条件同样是要满足x,p互质。
注:
欧拉函数: φ ( x ) = x ( 1 − 1 p ( 1 ) ) ( 1 − 1 p ( 2 ) ) ( 1 − 1 p ( 3 ) ) … . . ( 1 − 1 p ( n ) ) ( p ( i ) 表 示 x 的 第 i 个 质 因 子 ) φ(x)=x(1-\frac 1 {p(1)})(1-\frac 1 {p(2)})(1-\frac1 {p(3)})…..(1-\frac1 {p(n)})(p(i)表示x的第i个质因子) φ(x)=x(1−p(1)1)(1−p(2)1)(1−p(3)1)…..(1−p(n)1)(p(i)表示x的第i个质因子)
证明详见我的另一篇blog:传送门
代码如下:
int qsm(int x,int y){
int ans=1,w=x;
while (y) {
if (y%2==1) ans=(ans*w)%p;
y=y /2; w=(w*w)%p;
}
return ans;
}
int geteular(int num){
int ret=num,x=num;
for (int i=2;i*i<=num;i++)
if (x%i==0) {
ret=ret*(i-1)/i;
while (!x%i) x/=i;
}
if (x>1) ret=ret*(x-1)/x;
return ret;
}
int calc(int n,int p){
int p1=geteular(p);
return qsm(n,p1-1);
}
4.线性求逆元
当模数p比较小(可以用数组存储时)就可以线性求出范围内所有数的逆元。
我们来推导一下:
1 − 1 ≡ 1 ( m o d p ) 1^{−1}≡1 (mod p) 1−1≡1(modp)
设 p = k ∗ x + m p=k*x+m p=k∗x+m
带入 m o d p mod p modp的式子中:
k ∗ x + m ≡ 0 ( m o d p ) k*x+m≡0 (mod p) k∗x+m≡0(modp)
两边同乘 x − 1 ∗ p − 1 x^{−1}*p^{−1} x−1∗p−1就能得到:
k ∗ m − 1 + x − 1 ≡ 0 ( m o d p ) k*m^{−1} + x^{−1}≡0 (mod p) k∗m−1+x−1≡0(modp)
x − 1 ≡ − k ∗ m − 1 ( m o d p ) x^{−1}≡−k*m^{−1} (mod p) x−1≡−k∗m−1(modp)
x − 1 ≡ − ( p x ) ∗ ( p m o d x ) − 1 ( m o d p ) x^{−1}≡-(\frac p x) * (p \mod x)^{−1} (mod p) x−1≡−(xp)∗(pmodx)−1(modp)
那么就可以递推处理了。
代码如下:
for(int i=2;i<maxn;i++) inv[i]=(-p/i+p)*inv[p%i]%p;
##3.几个模版题
LG p3811
今天就写到这里,如果想到什么再补充。。。
Update 2018.7.20 加了一些latex公式,大大增强了可读性