求逆元的几种方法

求逆元的几种方法

博客借鉴来源

拓展欧几里得求逆元

原理

\(a*b\equiv 1 \pmod p\)
\(a*b+k*p = 1\)

代码

//拓展欧几里德求逆元
int exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1;y=0;
        return a;
    }
    int ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}
int getinv(int a,int m=mod){
    int x,y;
    int res=exgcd(a,m,x,y);
    return res==1? (x%m+m)%m:-1;
}

性能分析

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

费马小定理

原理

当p为素数时
\(a^{p-1}\equiv1\pmod p\)
\(a^{p-2}*a\equiv 1 \pmod p\)

代码

//费马小定理/快速幂求逆元
int ksm(int a,int b=mod-2,int m=mod){
    int res=1;
    while(b){
        if(b&1){
            res*=a;
            res%=m;
        }
        a*=a;
        a%=m;
        b>>=1;
    }
    return res;
}

性能分析

O(logmod)
适用范围:mod是个素数的时候。

欧拉函数求逆元

原理

当a和p互素时
\(a^{\varphi(p)}\equiv 1 \pmod p\)
\(a^{\varphi(p)}*a\equiv 1 \pmod p\)

代码

//欧拉函数求逆元
int phi(int x)
{
    int res=x,a=x;
    for(int i=2;i*i<=a;i++)
    {
        if(a%i==0)
        {
            res=res/i*(i-1);
            while(a%i==0)
                a=a/i;
        }
    }
    if(a>1)
        res=res/a*(a-1);
    return res;
}

int ksm(int a,int b=phi(mod)-1,int m=mod){
    int res=1;
    while(b){
        if(b&1){
            res*=a;
            res%=m;
        }
        a*=a;
        a%=m;
        b>>=1;
    }
    return res;
}

性能分析

O(logmod)
适用范围:mod是个合数的时候。

递推求逆元

原理

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

代码

int inv[mod+5];
void init(int mod){
    inv[1]=1;
    for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}

性能分析

时间复杂度O(n)
适用范围:mod数是不大的素数

posted @ 2021-07-17 10:45  碳素油墨  阅读(224)  评论(0)    收藏  举报