全心思齐网

5的逆元mod19计算方法?

逆元(Inverse element)就是在mod意义下,不能直接除以一个数,而要乘以它的逆元。

比如a∗b≡1(modp)a∗b≡1(modp),那么a,b互为模n意义下的逆元,比如你要算x/a,就可以改成x*b%p


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


注意

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


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

原理

a∗b≡1(modp)a∗b≡1(modp)

a∗b+k∗p=1a∗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;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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


性能分析:


时间复杂度:O(logn)(实际是斐波那契数列)

适用范围:只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法。

方法二:费马小定理/欧拉定理

原理

费马小定理:若p为素数,则有ap−1≡1(modp)ap−1≡1(modp)

ap−2∗a≡1(modp)ap−2∗a≡1(modp)

ap−2ap−2就是a在mod p意义下的逆元啦。


欧拉定理:若a、p互素,则有aφ(p)≡1(modp)aφ(p)≡1(modp)(费马小定理的一般形式)

aφ(p)∗a≡1(modp)aφ(p)∗a≡1(modp)

aφ(p)−1aφ(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);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

性能分析:


O(logmod)

适用范围:一般在mod是个素数的时候用,比扩欧快一点而且好写。

但是如果是合数,相信一般没人无聊到去算个欧拉函数。

方法三:递推求逆元

原理

p是模数,i是待求的逆元,我们求的是i−1i−1在mod p意义下的值

p=k∗i+rp=k∗i+r令 r < i,则k=p/i,r=p%i

k∗i+r≡0(modp)k∗i+r≡0(modp)

k∗r−1+i−1≡0(modp)k∗r−1+i−1≡0(modp)

i−1≡−k∗r−1(modp)i−1≡−k∗r−1(modp)

i−1≡−p/i∗inv[pmodi]i−1≡−p/i∗inv[pmodi]

嗯。。好难看的公式

说白了就是:inv[i]=-(mod/i)*inv[i%mod]

然后边界是inv[1]=1

这不仅为我们提供了一个线性求逆元的方法,也提供了一种O(logmod)求逆元的方法


代码

线性求逆元

LL inv[mod+5];

void getInv(LL mod)

{

inv[1]=1;

for(int i=2;i<mod;i++)

inv[i]=(mod-mod/i)*inv[mod%i]%mod;

}

1

2

3

4

5

6

7

1

2

3

4

5

6

7

注意:


调用前要先预处理

调用的时候要先对除数取mod

性能分析:


时间复杂度O(n)

适用范围:mod数是不大的素数而且多次调用,比如卢卡斯定理。

递归求逆元

LL inv(LL i)

{

if(i==1)return 1;

return (mod-mod/i)*inv(mod%i)%mod;

}

1

2

3

4

5

1

2

3

4

5

性能分析


时间复杂度:O(logmod)

好像找到了最简单的算法了!!

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

匿名回答于2021-01-30 19:20:05


相关知识问答