zoukankan      html  css  js  c++  java
  • 乘法逆元 学习总结

    基本都是抄的大神写好的东西,主要作为一个复习,加深印象。

    定义:若整数 b,m 互质,并且 b|a(b整除a),则存在一个整数 x,使得 a/b ≡ a * c (mod m)则称 x 为 b 的模 m 法逆元,记为 b-1(mod m)

    那么我们如何求 b-1(mod m)

    根据定义,a/b ≡ a * b-1 ≡ a/b * b * b-1 (mod m),那么 b * b-1 ≡ 1 (mod m)。

    到这里可以用两种方法求解b-1(mod m)

    1、费马小定理。

      费马小定理:若p是质数,则对于任意整数 a,有 ap ≡ a (mod p)。那么 bp ≡ b (mod p) → b * bp-1 ≡ b (mod p) → b * bp-2 ≡ 1 (mod p)。因此,当模数 p 为质数时,bp-2 即为 b-1(mod p) 。因此我们可以使用快速幂求出逆元。

    int qpow(int i,int k,int mod)
    {
        int res=1;
        while(k)
        {
            if(k&1) res=(LL)res*i%mod;
            i=(LL)i*i%mod;
            k>>=1;
        }
        return res;
    }
    
    int main()
    {
        int invb=qpow(b,p-2,p); //p是质数,b的逆元就是b^(p-2) mod p 
        return 0;
    }

    2、解同余方程。

      因为 b * b-1 ≡ 1 (mod m),那么同余方程 b * x ≡ 1 (mod m) 的解就是逆元,相当于解方程 b * x + m * y = 1,根据扩展欧几里得,只要保证了 b,m 互质,我们就可以用这种方法求出逆元。

    void exgcd(int a,int b,int &x,int &y)
    {
        if(b==0) {x=1;y=0;return;}
        exgcd(b,a%b,x,y);
        int z=x;x=y;y=z-a/b*y;
    }
    
    int main()
    {
        int x,y;
        exgcd(b,p,x,y);
        x=(x%p+p)%p;
        return 0;
    }

    当然还有其他特殊情况我们需要选用特殊的方法来求逆元:

    1、求解 1~n 这样一串连续数的逆元:

      已知 1-1 ≡ 1 (mod p)

      设 p = k * i + r,即 ⌊p/i⌋ = k,p%i = r

      那么 k * i + r ≡ 0 (mod p)

      左右同乘以i-1r-1,得 k * r-1 + i-1 ≡ 0 (mod p)

      移项,得 i-1 ≡ -k * r-1 (mod p)

      因为k = ⌊p/i⌋,r = p%i

      可得,i-1 = -⌊p/i⌋ * (p%i)-1 (mod p)

      为保证逆元为正数,令

      i-1 = (p-⌊p/i⌋) * (p%i)-1 (mod p)

      那么我们就可以利用这个式子递推出 1~n 的逆元。

    int main()
    {
        scanf("%d%d",&n,&p);
        inv[1]=1;
        for(int i=2;i<=n;i++) inv[i]=(LL)(p-p/i)*inv[p%i]%p;
        for(int i=1;i<=n;i++) printf("%d
    ",inv[i]);
        return 0;
    }

    2、求 1~n 的阶乘数的逆元

      因为$dfrac {1}{left( i+1 ight) !} imes left( i+1 ight) =dfrac {1}{i!}$

      设 inv[i] 为 i! 的逆元,那么 inv[i+1] * (i+1) = inv[i]

      利用这个关系我们就可以在知道 inv[n] 的情况下逆推出 1~n 的阶乘的逆元了。

    int qpow(int i,int k,int mod)
    {
        int res=1;
        while(k)
        {
            if(k&1) res=(LL)res*i%mod;
            i=(LL)i*i%mod;
            k>>=1;
        }
        return res;
    }
    
    int main()
    {
        scanf("%d%d",&n,&p);
        fact[0]=1;
        for(int i=1;i<=n;i++) fact[i]=(LL)fact[i-1]*i%p;
        invi[n]=qpow(fact[n],p-2,p);
        for(int i=n-1;i>=1;i--) invi[i]=(LL)invi[i+1]*(i+1)%p;
        return 0;
    }
  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/11515718.html
Copyright © 2011-2022 走看看