乘法逆元总结 3种基本方法

逆元

逆元(inverse element)是在取模意义下,不能直接除以一个数,而要乘以它的逆元;a*b ≡ \equiv 1 (mod p) , 那么a和b互为模p意义下的逆元,比如要计算(x/a)%p,可以写成x*b%p

方法一

费马小定理

若P为素数,则 a p − 1 {a^{p-1}} ap1 ≡ \equiv 1 (mod p) 【费马小定理】
即: a p − 2 {a^{p-2}} ap2 *a ≡ \equiv 1 (mod p)
所以 a p − 2 {a^{p-2}} ap2就是a在模p意义下的逆元

const LL mod = 1e9+7;
//快速幂
LL fastPow(LL a,LL b){
    LL ans=1;
    while(b){
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
//求x的逆元
LL inv(LL x){
    return fastPow(x,mod-2);
}

欧拉定理

若a和p互素(P不一定是素数),则 a ϕ ( p ) {a^{\phi{(p)}}} aϕ(p) ≡ \equiv 1 (mod p)
a ϕ ( p ) − 1 ∗ a {a^{\phi{(p)}-1}}*a aϕ(p)1a ≡ \equiv 1 (mod p)
所以 a ϕ ( p ) − 1 {a^{\phi{(p)}-1}} aϕ(p)1 就是a在模p意义下的逆元

联系

ϕ ( p ) {\phi{(p)}} ϕ(p)称为欧拉函数,表示小于等于P且与P互素的个数,显然若p为素数,则 ϕ ( p ) = p − 1 {\phi{(p)}}=p-1 ϕ(p)=p1

const LL mod = 1e9+7;
//求欧拉函数
long long phi(long long x)
{
    long long res = x;
    for(long long i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res = res/i*(i-1);//res -= res/i;
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)res =res/x*(x-1);//res -= res/x;
    return res;
}
//快速幂
LL fastPow(LL a,LL b){
    LL ans=1;
    while(b){
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
//求x的逆元
LL inv(LL x){
    return fastPow(x,phi(mod)-1);
}

方法二

扩展欧几里得算法

a*b ≡ \equiv 1 (mod p)
a * b+k * p = 1
a就是要求的逆元

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(LL a,LL mod)//求a在mod下的逆元,不存在逆元返回-1
{
    LL x,y;
    LL d=exgcd(a,mod,x,y);
    return d==1?(x%mod+mod)%mod:-1;
}

方法三:递推求逆元

P是模数,i是待求的逆元,我们求的是 i − 1 {i^{-1}} i1在mod P意义下的值
p=k*i+r,若(rk=p/i,r=p%i
k * i+r ≡ \equiv 1 (mod p)
两边同时除以i*r, k ∗ r − 1 + i − 1 ≡ 0 ( m o d P ) {k*r^{-1}+i^{-1} \equiv 0 (mod P) } kr1+i10(modP)
移项得: i − 1 ≡ − k ∗ r − 1 ( m o d P ) {i^{-1} \equiv -k*r^{-1} (mod P) } i1kr1(modP)
即: i − 1 ≡ − p / i {i^{-1} \equiv -p/i} i1p/i * inv[i % mod p]
边界条件是 inv[1]=1

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;
}

你可能感兴趣的:(数论)