乘法逆元的几种求法总结

乘法逆元

对于缩系中的元素,每个数a均有唯一的与之对应的乘法逆元x,使得ax≡1(mod n)
一个数有逆元的充分必要条件是gcd(a,n)=1,此时逆元唯一存在
逆元的含义:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x。

下面给出求逆元的几种方法

1 循环找解法

给定模m和需要求逆的数x,直接暴力枚举1~m-1
检查是否有x*i=1(mod m)

这种算法可以应用与写暴力、对拍、模数较小,求逆次数少的情况
时间复杂度O(m)

2 扩展欧几里得算法

给定模数m,求a的逆相当于求解ax=1(mod m)
这个方程可以转化为ax-my=1
然后套用求二元一次方程的方法,用扩展欧几里得算法求得一组x0,y0和gcd
检查gcd是否为1
gcd不为1则说明逆元不存在
若为1,则调整x0到0~m-1的范围中即可

void ext_gcd(int a,int b,int &d,int &x,int &y){
    if (b==0){
        d=a;x=1;y=0;
        return;
    }
    ext_gcd(b,a%b,d,y,x);//回代
    y-=a/b*x;
}
int main(){
    scanf("%d%d",&m,&n);//m关于n的逆元
    ext_gcd(m,n,d,x,y);
    printf("%d\n",(x+n)%n);
    return 0;
}

这种算法效率较高,常数较小,时间复杂度为O(ln n)

3 费马小定理及欧拉定理

在模为素数p的情况下,有费马小定理
a^(p-1)=1(mod p)
那么a^(p-2)=a^-1(mod p)
也就是说a的逆元为a^(p-2)

而在模不为素数p的情况下,有欧拉定理
a^phi(m)=1(mod m) (a⊥m)
同理a^-1=a^(phi(m)-1)

因此逆元x便可以套用快速幂求得了x=a^(phi(m)-1)

但是似乎还有个问题?如何判断a是否有逆元呢?
再求一次gcd判断是否互质吗?
这还不如直接用扩展欧几里得算法呢>_<

其实问题很简单,直接判断这是不是逆元就行了:
检验逆元的性质,看求出的幂值x与a相乘是否为1即可

这种算法复杂度为O(log2 n)
在几次测试中,常数似乎较上种方法大

4 O(n)求1~n逆元表

有时会遇到这样一种问题,
在模质数p下,求1~n逆元 n< p

这个问题有种很牛的算法,其基于以下的推导:
在求i的逆元时
p%i+[p/i]*i=p
令a=p%i,b=[p/i],则有
a+b*i=p
a+b*i=0(mod p)
b*i=-a(mod p)
i^-1=-b/a
也就是说i的逆元为:-[p/i]*(p%i)^-1
而p%i<i,那么可以从2递推到n求逆元,在求i之前p%i一定已经求出
这样就可以O(n)求出所有逆元了
(初始化 1^(-1)=1)
代码如下

    inv[1]=1;
    fo(i,2,n)
        inv[i]=(long long)(p-p/i)*inv[p%i]%p;

除了上面这种算法之外,还有几种O(n)的逆元表求法,不过没有上面这种方法简便。
A 可以先O(n)预处理出1!~n! 然后用O(log n)的复杂度求出n!的逆元
然后就可以O(n)处理出i!的逆元 (1/i!=(i+1)/(i+1)! 倒推即可)
接下来就可以O(n)求出所有i的逆元 1/i=(i-1)!/i!
需要同时预处理阶乘 阶乘的逆元的题目可以采用这种方法求逆元表。

这种方法扩展性比较强,可以用这种方法求出一个序列中所有元素(元素前缀积)的逆元,复杂度也是O(n),比一个个单独计算逆元效率更优。

B 可以用线性筛法。由于逆元是积性函数,合数的逆元可以O(1)计算,质数的逆元可以用O(log n)的复杂度求。由于n以内质数大约有n/log n个。总复杂度也是O(n)

调试问题

最后再简单讲讲如何调试,检测求得的逆元是否正确
还是同上面所说的那样,直接测试其与原数相乘是否为1即可了

这里顺便再啰嗦一句:如何检验快速幂算法是否正确呢(这还用检验)
随便找1个质数p,检测a^(p-1)%p是否都为1就行啦 (1<=a<p)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值