1. 程式人生 > >乘法逆元總結 3種基本方法

乘法逆元總結 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}} \equiv 1 (mod p) 【費馬小定理】
即: a
p 2 {a^{p-2}}
*a \equiv 1 (mod p)
所以 a p 2 {a^{p-2}} 就是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)}}} \equiv 1 (mod p)
a ϕ ( p ) 1 a {a^{\phi{(p)}-1}}*a \equiv 1 (mod p)
所以 a ϕ ( p ) 1 {a^{\phi{(p)}-1}} 就是a在模p意義下的逆元

聯絡

ϕ ( p ) {\phi{(p)}} 稱為尤拉函式,表示小於等於P且與P互素的個數,顯然若p為素數,則 ϕ ( p ) = p 1 {\phi{(p)}}=p-1

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}} 在mod P意義下的值
p=k*i+r,若(r<i),則有k=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) }
移項得: i 1 k r 1 ( m o d P ) {i^{-1} \equiv -k*r^{-1} (mod P) }
即: i 1 p / i {i^{-1} \equiv -p/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;
}