乘法逆元

我们知道,由于同余的运算只定义在整数集中,而整数集不满足除法封闭,所以同余是不满足同除性的。但是,如果有涉及取模操作的计数类题目当中需要除法运算怎么办,这就需要乘法逆元了。

定义

若整数\(b,m\)互质,且\(b|a\),则存在一个整数\(x\),满足\(\frac{a}{b}\equiv ax(mod\ m)\),称\(x\)\(b\)在模\(m\)意义下的乘法逆元,即为\(b^{-1}(mod\ m)\)

通常来说,我们会使用另一种方式来表示逆元:
\[\frac{a}{b}\equiv ab^{-1}\equiv \frac{a}{b}*b*b^{-1}(mod\ m)\]
故对于逆元\(b^{-1}(mod\ m)\),满足\(b*b^{-1}\equiv 1(mod\ m)\)

求解

了解了模意义下乘法逆元的定义和作用,那么我们需要知道如何求解。通常来说,我们有好几种常用的方式,如费马小定理,扩展欧几里得等,接下来我们一一详解。

费马小定理

注意到著名的费马小定理和我们的逆元非常像,可以推导一下:
\[a^p\equiv a(mod\ p)\\a^{p-1}\equiv 1(mod\ p)\\a*a^{p-2}\equiv 1(mod\ p)\]
所以,当模数\(p\)为质数时,\(a^{p-2}\)就是\(a\)在模\(p\)意义下的乘法逆元。

\(a^{p-2}\)显然是可以用快速幂算的,那么我们就得到了逆元的第一种解决方案,可以求解单独一个数的逆元,时间复杂度为\(O(log_2p)\)

\(Code:\)

inline long long power(long long a,long long b,long long p)
{
    long long res=1;
    while (b)
    {
        if (1&b)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
inline long long Fermat(long long a,long long p)
{
    return power(a,p-2,p);
}

欧拉定理

费马小定理求逆元的话是要求模数\(p\)为质数的,更一般地,如果只能保证\(a,p\)互质,那么我们可以用欧拉定理求逆元。
\[a^{\phi(p)}\equiv 1(mod\ p)\\a*a^{\phi(p)-1}\equiv 1(mod\ p)\]

那么\(a^{\phi(p)-1}\)就是\(a\)在模\(p\)意义下的一个乘法逆元。

欧拉函数可以用试除法分解质因数求,那么这就是求解逆元的第二种方法,可以求解单独一个数的逆元,时间复杂度\(O(\sqrt p+log_2p)\)

\(Code:\)

inline long long power(long long a,long long b,long long p)
{
    long long res=1;
    while (b)
    {
        if (1&b)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
inline long long phi(long long x)
{
    long long ans=x;
    for (long long i=2;i*i<=x;i++)
    {
        if (x%i==0)
        {
            ans=ans/i*(i-1);
            while (x%i==0)x/=i;
        }
    }
    if (x>1)ans=ans/x*(x-1);
    return ans;
}
inline long long Eular(long long a,long long p)
{
    return power(a,phi(p)-1,p);
}

线性同余方程

我们已经知道了只有\(a,p\)互质时逆元\(a^{-1}\)的求法,但是试除法求解欧拉函数未免有点慢。考虑到我们已经得到了逆元的表达方式,其实我们可以直接通过解线性同余方程的方法入手,求解逆元。

已知逆元\(a^{-1}(mod\ p)\)满足\(a*a^{-1}\equiv 1(mod\ p)\),令\(x=a^{-1}\),则\(ax\equiv 1(mod\ p)\)

这是一个简单的线性同余方程,设\(-py=ax-1\),则可以将方程改写为\(ax+py=1\),利用扩展欧几里得算法找到解。

这就是求解逆元的第三种方法,以求解单独一个数的逆元,时间复杂度\(O(log_2a+log_2p)\)

\(Code:\)

inline long long Exeuclid(long long a,long long &x,long long b,long long &y,long long c)
{
    if (!b){x=c/a,y=0;return a;}
    else
    {
        long long p=Exeuclid(b,x,a%b,y,c);
        long long x_=x,y_=y;
        x=y_,y=x_-a/b*y_;
        return p;
    }
}
inline long long Euclid(long long a,long long p)
{
    long long x_,y_;
    Exeuclid(a,x_,p,y_,1);
    return (x_+p)%p;
}

逆元递推

如果求的是单独一个数的逆元的话,那么以上的算法基本上可以完美解决了。现在,如果需要快速的求解\(1-n\)每一个数模\(p\)意义下的逆元,我们就需要要到逆元递推了。

证明:

\(t=\lfloor \frac{p}{x} \rfloor\)\(k=p\ mod\ x\),即\(p=tx+k\)

那么显然有\(tx+k\equiv 0(mod\ p)\),则\(-tx\equiv k(mod\ p)\)

将上式两边同时除以\(x*k\),则\(-t*k^{-1}\equiv x^{-1}(mod\ p)\),进而得到\(x^{-1}=(p-\frac{p}{x})*(p\ mod\ x)^{-1}\ mod\ p\)

已知\(1^{-1}=1\),那么剩下的递推就行了,可以求解\(1-n\)所有数的逆元,时间复杂度\(O(n)\)

\(Code:\)

inline long long recursion(int n)
{
    inv[1]=1;
    for (int i=2;i<=n;i++)
        inv[i]=(p-p/i)*inv[p%i]%p;
}

逆元递归

当然,利用上述引理,我们也可以直接递归来求解单独一个数的逆元,时间复杂度为\(O(log_2a)\)

\(Code:\)

inline long long inv(long long x,long long p)
{
    if (x==1)return 1;
    else return (p-p/x)*inv(p%x)%p;
}

总结

至此,本文已经介绍了\(5\)种求乘法逆元的方案,其中每一种方法都有各自的优缺点,希望读者能够灵活运用,选择合适的方法来求解逆元。


04-13 11:57