深入浅出乘法逆元

浅析乘法逆元

1.模的运算律

先来一波模运算律表:

运算律内容
交换律 (a+b)%p=(b+a)%p
(a×b)%p=(b×a)%p
结合律 ((a+b)%p+c)%p=(a+(b+c)%p)%p
((a×b)%p×c)%p=(a×(b×c)%p)%p
分配率 ((a+b)%p×c)%p=((a×c)%p+(b×c)%p)%p
(a×b)%p=(a%p×b%p)%p
(a+b)%p=(a%p+b%p)%p
(ab)%p=(a%pb%p)%p

2.定义

有的时候我们需要对一个数取模,这很简单。但是在取模的过程中出现了除数,那么取模就没这么简单了:

72%4=3%4=3
注意: 7%42=32=1 错误的
但万一是 7100002%4 计算机可无法先计算 7100002 (mod4) ,因为数字太大了。
这个时候我们就需要用到乘法逆元了,事实上: 7100002=(73)10000 。我们运用模的运算律可以通过边乘边取模即可得到答案,其中3是7在 (mod4) 意义下的逆元。
关于逆元的严格定义如下:

b,m,bax使a/bax(modm)xbmb1(modm)

3.求解

3.1费马小定理1

因为 a/bab1a/bbb1(modm) ,所以 bb11(modm) 2
如果m是质数(此时我们用符号 p 代替m)并且 b<p ,根据费马小定理 bp11(modp) ,即 bbp21(modp) 。因此,当模数 p 为质数时,bp2 b 的乘法逆元。
到最后我们可以用快速幂来迅速求出bp2。代码如下:

int ksm(int a,int b,int p) {
    int ans=1;
    for(; b; b>>=1,a=a*a%p)if(b&1)ans=ans*a%p;
    return ans;
}

时间复杂度是 O(logn)

3.2扩展欧几里得算法

扩展欧几里得算法的具体内容参考我写的:浅析扩展欧几里得算法(exgcd)
根据逆元的定义我们要求的是 ax1(modm) 关于x的同余方程,其中x为a在 (modm) 意义下的逆元
事实上

ax=my+1
也就是 ax÷m=y1
变形①式得:
axmy=1

既然 a,b 都已知,就不难求出 xy 了(但要注意 y 的系数b必须是正整数,因为在计算机计算过程中如果模数是负的将导致结果出错)如果 b 不是正整数,我们同时改变 a,b 的符号即可。扩展欧几里得算法代码如下:

int exgcd(int a,int b,int &x,int &y) {
    if(b) {
        int c=exgcd(a,b,y,x);
        y-=a/b*x;
        return c;
    } else {
        x=1;
        y=0;
        return a;
    }
}

时间复杂度是 O(lnn)

3.3线性求解

当我们需要求解大量的逆元的时候,前两种的方法时间复杂度都要乘以 n ,时间复杂度都不是很理想。所以我们就用O(n)的时间来快速求解。具体做法如下:假设我们要求x的逆元,那么:

m=kx+r
kx+r0(modm)
同乘以 x1r1 得:
kr1+x10(modm)
将②式变形得:
x1kr1(modm)

所以我们得到:
x1=m/x(m%x)1

那么只要建一个数组inv,初始值inv[1]=1。所以代码如下:

    for(int i=2; i<=n; i++)
        inv[i]=-(p/i)*inv[p%i];

  1. 这种方法的常数比较大。
  2. b1b1(modm) 其中前一个是 ÷b ,后一个是 a 的逆元b;根据模的运算律 (a/bbb1)%m=((a/b)%m((bb1)%m)%m ,显然 (bb1)%m=1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值