逆元的求法总结(3种基本方法+4种实现)

您所在的位置:网站首页 阶乘有什么简便方法计算吗 逆元的求法总结(3种基本方法+4种实现)

逆元的求法总结(3种基本方法+4种实现)

2024-07-14 21:57| 来源: 网络整理| 查看: 265

简述逆元

逆元(Inverse element)就是在mod意义下,不能直接除以一个数,而要乘以它的逆元。 比如 a ∗ b ≡ 1 ( m o d p ) a*b≡1 (mod p) a∗b≡1(modp),那么a,b互为模n意义下的逆元,比如你要算x/a,就可以改成x*b%p

观察 a ∗ b ≡ 1 ( m o d p ) a*b≡1 (mod p) a∗b≡1(modp),变形为 a ∗ b + k ∗ p = 1 a*b + k*p =1 a∗b+k∗p=1,就可以用扩展欧几里得算法求a了,同时这里也说明了a和p只有在互素的情况下才存在逆元。

注意

在下面所有的算法中,最好先把除数取个模再运算。

方法一:扩展欧几里得算法 原理

a ∗ b ≡ 1 ( m o d p ) a*b≡1 (mod p) a∗b≡1(modp) a ∗ b + k ∗ p = 1 a*b + k*p =1 a∗b+k∗p=1 然后a就是我们要求的逆元,最终得到一个正数a的话就要对a mod p,因为a加上mp的时侯k减少mb可以使得等式依然成立。

如果你不想让逆元为正数,那么直接返回x也是可以正确的逆元

代码 LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法 { if(b==0) { x=1,y=0; return a; } LL ret=exgcd(b,a%b,y,x); y-=a/b*x; return ret; } LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1 { LL x,y; LL d=exgcd(a,mod,x,y); return d==1?(x%mod+mod)%mod:-1; }

注意:返回的时候可以改成(x+mod)%mod,因为扩展欧几里得算法算出来的x应该不会太大.

性能分析:

时间复杂度:O(logn)(实际是斐波那契数列)适用范围:只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法。 方法二:费马小定理/欧拉定理 原理

费马小定理:若p为素数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(mod p) ap−1≡1(modp) a p − 2 ∗ a ≡ 1 ( m o d p ) a^{p-2}*a≡1(mod p) ap−2∗a≡1(modp) a p − 2 a^{p-2} ap−2就是a在mod p意义下的逆元啦。

欧拉定理:若a、p互素,则有 a φ ( p ) ≡ 1 ( m o d p ) a^{φ(p)}≡1(mod p) aφ(p)≡1(modp)(费马小定理的一般形式) a φ ( p ) ∗ a ≡ 1 ( m o d p ) a^{φ(p)}*a≡1(mod p) aφ(p)∗a≡1(modp) a φ ( p ) − 1 a^{φ(p)-1} aφ(p)−1就是a在mod p意义下的逆元啦。

代码 LL qkpow(LL a,LL p,LL mod) { LL t=1,tt=a%mod; while(p) { if(p&1)t=t*tt%mod; tt=tt*tt%mod; p>>=1; } return t; } LL getInv(LL a,LL mod) { return qkpow(a,mod-2,mod); }

性能分析:

O(logmod)适用范围:一般在mod是个素数的时候用,比扩欧快一点而且好写。但是如果是合数,相信一般没人无聊到去算个欧拉函数。 方法三:递推求逆元 原理

p是模数,i是待求的逆元,我们求的是 i − 1 i^{-1} i−1在mod p意义下的值 p = k ∗ i + r p=k*i+r p=k∗i+r令 r < i,则k=p/i,r=p%i k ∗ i + r ≡ 0 ( m o d p ) k*i+r≡0(mod p) k∗i+r≡0(modp) k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k*r^{-1}+i^{-1}≡0(mod p) k∗r−1+i−1≡0(modp) i − 1 ≡ − k ∗ r − 1 ( m o d p ) i^{-1}≡-k*r^{-1}(mod p) i−1≡−k∗r−1(modp) i − 1 ≡ − p / i ∗ i n v [ p m o d i ] i^{-1}≡-p/i*inv[p mod i ] i−1≡−p/i∗inv[pmodi] 嗯。。好难看的公式 说白了就是:inv[i]=-(mod/i)*inv[mod%i] 然后边界是inv[1]=1 这不仅为我们提供了一个线性求逆元的方法,也提供了一种O(logmod)求逆元的方法

代码 线性求逆元 LL inv[mod+5]; void getInv(LL mod) { inv[1]=1; for(int i=2;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3