【乘法逆元】取模运算

一,定义

1 逆元:
	在群G中,∀a∈G,∃a′∈G,s.t.aa′=e,其中e为G的单位元。
2 乘法逆元:

p为素数,记a⋅b=a×bmodp在群(N,⋅)(N,·)中,∀a∈N,∃a′∈N,s.t.aa′=e=1∀a∈N,∃a′∈N,s.t.aa′=e=1。则称a′是a关于modp的逆元。
为了方便表示,且下面的内容都只涉及到相同的p,我们记a关于modp的逆元为inv[a]。

二, 作用

情况1:

在算法竞赛中,经常会遇到求解数据很大,则输出模 [公式] 的解这类要求。加法、减法、乘法等操作,基于同余理论直接取模即可。但遇到除法时,某步中间结果不一定能完成整除,就无法求解了。
对于(36/3%7)的情况。
第一步,3
6=18,18%7=4。
第二步,4/3无法整除,但是我们可以求出除数3关于模数7的逆元5,35%7=1符合逆元的定义。
所以我们用
5代替/3,即4*5=20,而20%7=6,得到正确答案。

*** 对于一个大数m,在计算m/a%b时,对于a的逆元d可以有m*d%b=m/a%b。对于需要在计算中取模并且有除法的题目中非常好用。 ***

情况2:

当我们要求ab的值时,知道答案的范围ans≤k,但是a和b太大无法直接计算。这时候我们就要假设出一个质数L>k,然后求abmodL即可。

三, 求法

1.枚举法
2.快速幂(费马小定理)
欧拉定理,a^ϕ(p)=1(modp)

∴ap−1≡1(modp)
∴a^p−1≡1(modp)
∴a×a(p−2)≡1(modp)∴a×a^(p−2)≡1(modp)
根据逆元的存在唯一性,inv[a]≡a^p−2(modp)
** 适用:如果n nn是一个质数,而整数a aa不是n nn的倍数**

#include <cstdio>
int a,p;
int Pow(int i,int j) {
	if (!j) return 1;
	int now=Pow(i,j>>1);
	now=now*now%p;
	if (j&1) now=now*i%p;
	return now;
}
int main(void) {
	scanf("%d%d",&a,&p);
	printf("%d\n",Pow(a,p-2));
	return 0;
}
3.扩展欧几里得算法

aa′≡1(modp) ,我们只用找到ax+py=1(ax=-py+1),而a′≡x。
对于ax+py=1中x,y的找法就是扩展欧几里得算法。
我们对a,p最大公约数,顺便维护x和y的值。
** 适用:存在逆元即可求,适用于个数不多但模数b很大的时候,最常用、安全的求逆元方式。**

#include <cstdio>
int a,p;
int x,y;
void exgcd(int a,int b)
{
    if (!b) {x=1,y=0;return;}
    exgcd(b,a%b);
    int _x=x,_y=y;
    x=_y,y=_y*(a/b)-_x;
    x%=p,y%=p;
}
int main(void)
{
    scanf("%d%d",&a,&p);
    exgcd(a,p);
    printf("%d\n",(x+p)%p);
    return 0;
}
4.线性递推求逆元

** 公式: i n v [ i ] = ( p − p / i ) × i n v [ p % i ] % p , i n v [ 1 ] = 1 **
** 用途:可以 O ( n ) 预处理出 [1,n] 所有数的关于模 p 的乘法逆元 **
**适用范围:mod数是不大的素数而且多次调用,比如卢卡斯定理。 **

long long inv[N];
int main(){
	int n,p;
	cin>>n>>p;
	inv[1]=1;
	for(int i=2;i<=n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
	for(int i=1;i<=n;i++)printf("%d\n",inv[i]);
	return 0;
} 

4,递归求逆元

用途: mod数是素数,所以并不好用,比如中国剩余定理中就不好使,因为很多时候可能会忘记考虑mod数是不是素数。

LL inv(LL i)
{
	if(i==1)return 1;
	return (mod-mod/i)*inv(mod%i)%mod;
}

5,线性求逆元

当用逆元的次数比较多时,一直l o g loglog求可能会T TT,这时线性求出来逆元是个不错的选择

void init()
{
    inv[1] = 1;
    for (int i = 2; i <= N; i ++)
        inv[i] = mul(dec(mod, mod / i), inv[mod % i]); 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值