乘法逆元【解法大全】

定义

a ∗ x ≡ 1 ( m o d    p ) a*x≡1 (mod\;p) ax1(modp),且 a a a p p p 互质,那么我们就能定义: x x x a a a 的逆元,记为 a − 1 a^{-1} a1,所以我们也可以称 x x x a a a 的倒数( m o d mod mod p p p 意义下)。

所以对于 a b      m o d    p \frac{a}{b}\ \; mod\;p ba modp ,我们就可以求出 b 在 m o d    p mod\; p modp意义下的逆元,然后乘上 a a a ,再 m o d    p mod \; p modp ,就是这个乘法逆元的值了。


模板题

在这里插入图片描述
原题链接》》乘法逆元


法一

两种证明:

(1)费马小定理

定理内容:如果 a , p a,p a,p 互质,那么 a p − 1 ≡ 1    ( m o d    p ) a^{p-1} ≡ 1 \;(mod\; p) ap11(modp)

把上式转换成 a ∗ a p − 2 ≡ 1    ( m o d    p ) a*a^{p-2} ≡ 1 \;(mod\; p) aap21(modp),结合逆元方程 a ∗ x ≡ 1    ( m o d    p ) a*x ≡ 1 \;(mod\; p) ax1(modp),得到 a ∗ a p − 2 ≡ a ∗ x    ( m o d    p ) a*a^{p-2} ≡ a*x \;(mod\; p) aap2ax(modp)

所以 a p − 2 ≡ x    ( m o d    p ) a^{p-2} ≡ x \;(mod\; p) ap2x(modp),即 x = a p − 2    m o d    p x = a^{p-2} \;mod\; p x=ap2modp, 用快速幂求解即可

(2)欧拉定理
定理内容:如果a,p互质,那么 a φ ( p ) ≡ 1 ( m o d    p ) a^{φ(p)}≡1(mod\;p) aφ(p)1(modp),当p为质数时, φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p1
所以 a p − 1 ≡ 1 ( m o d    p ) a^{p-1}≡1(mod\;p) ap11(modp),跟费马小定理的式子长得一模一样,按刚才的方式推导即可得 x = a p − 2    m o d    p x = a^{p-2} \;mod\; p x=ap2modp

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

ll n,p;

ll ksm(ll a,ll b)
{
	ll ans=1,base=a;
	while(b)
	{
		if(b&1)
			ans=(ans*base)%p;
		base=(base*base)%p;
		b>>=1;
	}
	return ans;
}

int main(){
	scanf("%lld%lld",&n,&p);
	for(ll i=1;i<=n;i++)
	{
		printf("%lld\n",ksm(i,p-2)%p);
	}
}

法二

解不定方程(扩欧)

解同余方程 a x ≡ 1    ( m o d    p ) ax≡1 \;(mod \;p) ax1(modp)等价于解不定方程 a x + p y = 1 ax+py=1 ax+py=1

根据裴蜀定理,这个不定方程有整数解的前提是gcd(a,p)=1,即a,p互质。(而保证互质的一个方法是令a或b为一个大质数,比如998244353或者1e9+7之类的,所以如果题目莫名给了一个大质数或者a,b互质的条件,很可能就是在保证扩欧方程有解的前提)

E x g c d Exgcd Exgcd求解即可(不会 请左转 Exgcd

code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

ll n,p,x,y;

void exgcd(int a,int b)
{
	if(b==0)
	{
		x=1,y=0;
		return;
	}
	exgcd(b,a%b);
	int z=x;
	x=y,y=z-(a/b)*y;
}

int main(){
	scanf("%lld%lld",&n,&p);
	for(ll i=1;i<=n;i++)
	{
		exgcd(i,p);
		x=(x%p+p)%p;
		printf("%lld\n",x); 
	}
}

法三

线性递推:复杂度 O ( n ) O(n) O(n)!!!

过程:
p = k ∗ i + r p=k*i+r p=ki+r ; ( k = p / i , r = p    m o d    i    ) (k=p/i,r=p\;mod\;i\;) (k=p/ir=pmodi),其中 ( i < p , k < p , r < i ) (i<p,k<p,r<i) (i<p,k<p,r<i),则有 :

  • k ∗ i + r ≡ 0    ( m o d    p ) k*i+r≡0\;(mod\; p) ki+r0(modp)

①式左右同乘 i − 1 ∗ r − 1 i^{-1}*r^{-1} i1r1得:

  • k ∗ r − 1 + i − 1 ≡ 0    ( m o d    p ) k*r^{-1}+i^{-1} ≡ 0 \;(mod \;p) kr1+i10(modp)移项 得

移项 得

  • i − 1 ≡ − k ∗ r − 1    ( m o d    p ) i^{-1} ≡ -k*r^{-1} \;(mod\; p) i1kr1(modp)

带入 k = p / i , r = p    m o d    i    k=p/i,r=p\;mod\;i\; k=p/ir=pmodi

  • i − 1 ≡ ( − p i ) ∗ ( p    m o d    i ) − 1      ( m o d    p ) i^{−1} ≡ (-\frac{p}{i})*(p\;mod \;i)^{-1} \;\;(mod\; p) i1(ip)(pmodi)1(modp)

由于 ( p    m o d    i ) < i (p\; mod\; i) < i (pmodi)<i 所以,在求出 i − 1 i^{-1} i1之前,我们早已求出 ( p    m o d    i ) − 1 (p\; mod \;i)^{-1} (pmodi)1因此用数组 i n v [ i ] 记录 i − 1 inv[i] 记录i^{-1} inv[i]记录i1 ( i i i 的逆元),则

  • i n v [ i ] ≡ ( − p i ) ∗ i n v [ p    m o d    i ]    m o d    p inv[i]≡ (-\frac{p}{i})*inv[p\;mod \;i] \;mod\; p inv[i](ip)inv[pmodi]modp

不要以为到这里就结束了

因为我们需要保证 i − 1 > 0 i^{-1}>0 i1>0所以,我们在②式右边    + p    ( p    m o d    p = 0 \;+p\; ( p\;mod\;p=0 +p(pmodp=0 , 答案不变)

i n v [ i ] = p − p i   ∗ i n v [ p    m o d    i ]      m o d    p inv[i]=p-\frac{p}{i}\ * inv[p\;mod\;i]\;\;mod\;p inv[i]=pip inv[pmodi]modp

当然 i n v [ 1 ] = 1 , i n v [ 0 ] = t a n 90 inv[1]=1,inv[0]=tan90 inv[1]=1,inv[0]=tan90(赋值为0);

而且 f o r for for循环 要从 2 2 2开始,防止改变 i n v [ 1 ] inv[1] inv[1] 的值;

code:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

ll n,p,x,y,inv[3000010];

int main(){
	scanf("%lld%lld",&n,&p);
	inv[1]=1;
	printf("%lld\n",inv[1]);
	for(ll i=2;i<=n;i++)
	{
		inv[i]=p-(p/i)*inv[p%i]%p;
		printf("%lld\n",inv[i]);
	}
}

应用

一般用于求 a b      m o d    p \frac{a}{b}\ \;mod\; p ba modp ,把它转换为求a*(b的逆元)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值