逆元应用与证明
在学习逆元之前我们先来了解一下同余的概念:
简单来讲就是整数a mod(m)=b mod(m) ,写做
a
≡
b
(
m
o
d
m
)
a\equiv b(mod \ m)
a≡b(mod m)。
再来看一下逆元的定义:
简单来说就是:
a
∗
x
≡
1
(
m
o
d
m
)
a*x\equiv 1(mod \ m)
a∗x≡1(mod m) (gcd(a,b)==1,也就是a要与b互质) 这时,我们称
a
a
a在模
m
m
m下的逆元为
x
x
x,同样
x
x
x的逆元为
a
a
a,记作 inv(
x
x
x)=
a
a
a。
a a a在模 m m m下的逆元存在的充分必要条件是 a a a与 m m m互质,即gcd(a,m)=1
那么逆元有什么运用呢?
一方面是求余:
(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)
这时我们就需要用到逆元来解决了。
那么怎么解决呢?
a
∗
x
≡
1
(
m
o
d
p
)
a*x \equiv 1(mod \ p)
a∗x≡1(mod p) -----------(1)
那么,在数论上
x
=
1
a
(
m
o
d
p
)
x=\frac{1}{a}(mod \ p)
x=a1(mod p)
那么
(
a
b
)
%
p
=
a
∗
x
%
p
(\frac{a}{b}) \% \ p=a*x\% \ p
(ba)% p=a∗x% p这时就可以直接得出结果了。
我们来证明一下这个结论:
假设
(
a
b
)
%
p
=
m
(\frac{a}{b}) \% \ p=m
(ba)% p=m----------(2)
两边同时乘于 b b b: a % p = m ∗ b % p a\%p=m*b\%p a%p=m∗b%p------------(3)
再同时乘与 x x x: a ∗ x % p = m ∗ b ∗ x % p a*x\%p=m*b*x\%p a∗x%p=m∗b∗x%p----------(4)//注意这里的x是b(mod p)的逆元,即 b ∗ x ≡ 1 ( m o d p ) b*x \equiv 1(mod \ p) b∗x≡1(mod p)
由(1)可得: a ∗ x % p = m % p a*x\%p=m\%p a∗x%p=m%p---------(5)
所以我们可以得到 ( a b ) % p = a ∗ x % p (\frac{a}{b}) \% \ p=a*x\% \ p (ba)% p=a∗x% p
那么如何求一个数的逆元呢?
有两种方法:
1.快速幂(费马小定理)
当p为质数时,且a不为p的倍数(a,p一定互质),那么可以用费马小定理来求解,费马小定理指出
当p为质数时,且a不为p的倍数时 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1(mod\ p) ap−1≡1(mod p),左边变形可得
a ∗ a p − 2 ≡ 1 ( m o d p ) a*a^{p-2}\equiv1(mod\ p) a∗ap−2≡1(mod p)等价于 a ∗ x ≡ 1 ( m o d p ) , x = a p − 2 a*x\equiv1(mod\ p),x=a^{p-2} a∗x≡1(mod p),x=ap−2
这里 a a a的逆元inv( a a a)= a p − 2 a^{p-2} ap−2,可以用快速幂来求 a p − 2 a^{p-2} ap−2
时间复杂度为 O ( l o g n ) O(log n) O(logn)
当p不为质数,但是a,p互质时(不互质逆元不存在),可以根据欧拉定理来求逆元,欧拉定理是对
费马小定理的扩展,用欧拉定理时需要先求欧拉数 φ ( p ) \varphi(p) φ(p),欧拉定理是 a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)}\equiv1(mod\ p) aφ(p)≡1(mod p),因为质数的欧拉数为其自身-1,所以费马小定理求逆元不用求额外求欧拉数,不过一般p不为质数时,用扩展欧几里得算法来求逆元
扩展欧几里得算法
扩展欧几里得算法就是辗转相除法,在辗转相除时要通过回溯来找系数
费马小定理方法
费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(mod \ p) ap−1≡1(mod p)
费马小定理要求
p
p
p为质数,而逆元的定义要求
g
c
d
(
a
,
p
)
=
1
gcd(a,p)=1
gcd(a,p)=1,所以当我们用费马小定理求解逆元时,前提条件是
p
p
p为质数,之后只需要保证
a
a
a不是
p
p
p的倍数(>=1)即可。
求解方法:
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}≡1(mod \ p)
ap−1≡1(mod p)=>
a ∗ a p − 2 ≡ 1 ( m o d p ) a*a^{p-2}≡1(mod \ p) a∗ap−2≡1(mod p)
此时a的逆元 x = a p − 2 x=a^{p-2} x=ap−2,我们可以利用快速幂来求出 a p − 2 a^{p-2} ap−2
给定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≤1e5,
1≤ai,pi≤2∗1e9
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible
分析:这是一道用费马小定理来求逆元的模板题,只需要判断ai是否是pi的倍数,然后用快速幂求出 a i p − 2 {ai}^{p-2} aip−2即可。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n,a,p;
int abc()
{
int res=1;
int b=p-2;
while(b)
{
if(b&1) res=(ll)res*a%p;
b>>=1;
a=(ll)a*a%p;
}
return res;
}
int main()
{
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&a,&p);
if(a%p==0) printf("impossible\n");
else printf("%d\n",abc());
}
return 0;
}
扩展欧几里得求逆元
首先了解一下扩展欧几里得算法,扩展欧几里得算法求逆元要保证
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1,条件要比快速幂要宽一些。
a
x
≡
1
(
m
o
d
b
)
ax\equiv1(mod\ b)
ax≡1(mod b)
a
x
−
k
b
=
1
ax-kb\ =1
ax−kb =1这样是不是就很眼熟了(
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b))
事实上就可以写成
a
x
+
b
y
=
1
ax+by=1
ax+by=1
那么求逆元的方法就有了,就是求解
x
\ \ x
x。
这里给出扩展欧几里得算法的模板代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
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);//a,b交换位置,x,y的位置也改变
y-=a/b*x;
return d;
}
int main()
{
scanf("%d",&n);
while(n--)
{
int a,b,x,y;
scanf("%d%d",&a,&b);
int k=exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}
可以求得 x , y x,y x,y, x x x为 a a a的逆元。
打表求逆元
用来求1~p-1(mod p)的逆元
递推式:
inv[i] = ( p - p / i ) * inv[p % i] % p
证明:
令 p=k*i+r,其中1<i<p,r<i
则 p
≡
\equiv
≡ 0(mod p)
即 k
∗
*
∗i+r
≡
\equiv
≡ 0(mod p),两边同时乘于inv[i]和inv[r]可得
k
∗
*
∗inv[r]+inv[i]
≡
\equiv
≡ 0(mod p)
inv[i] = -k
∗
*
∗inv[r](mod p),k=p/i(整除)
inv[i] = -p/i
∗
*
∗inv[r](mod p)
要保证inv[i]为正整数,所以inv[i] =(p -p/i)
∗
*
∗inv[r](mod p)
特殊的,inv[1]=1,i从2开始打表
逆元打表求1!~n!
递推公式:
inv[i] = inv[i + 1] * (i + 1) % MOD
反着递推,首先用费马小定理或者扩展欧几里得求出n!的逆元,
n
!
−
1
∗
n
=
(
n
−
1
)
!
−
1
n!^{-1}*n=(n-1)!^{-1}
n!−1∗n=(n−1)!−1,从大到小递推即可