数论——逆元

逆元

引言

(a*b)%m=(a%m*b%m)%m
但是 a b {\frac{a}{b}} ba%m ≠ a % m b % m {\frac{a\%m}{b\%m}} b%ma%m%m

有一个方法可以求,但对b,m有限制。

a b {\frac{a}{b}} ba%m= a % ( b ∗ m ) b {\frac{a\%{(b*m)}}{b}} ba%(bm)

设 k= ⌊ a b ÷ m ⌋ {\lfloor{\frac{a}{b}\div m}\rfloor} ba÷m ,x = a b % m {\frac{a}{b}\% m} ba%m

a b {\frac{a}{b}} ba=k*m+x
⇒ a=k*m*b+x*b
⇒ a%(m*b)=x*b
⇒ x= a % ( m ∗ b ) b {\frac{a\%(m*b)}{b}} ba%(mb)
a b % m {\frac{a}{b}\% m} ba%m= a % ( m ∗ b ) b {\frac{a\%(m*b)}{b}} ba%(mb)
如果m*b很大甚至会爆long long , 这时就需要用另一种方法来完成。

当要求取模时, a b {\frac{a}{b}} ba答案可能为小数无法直接取模,那么此时就需要一个东西将分数转化为乘法的形式以帮助我们取模,这个“东西”就是逆元。

a b {\frac{a}{b}} ba%m = (a*inv(b))%m(此时inv(b)为b的逆元)

1、定义

由上可知,决定一个数的逆元不仅取决它本身还取决取模的数。

条件:x存在乘法逆元的充要条件是x与模数m互质

inv(x) ≡ {\equiv} x − 1 {^{-1}} 1(%m)
x*inv(x) ≡ {\equiv} 1(%m)

以后面对除法取模即可通过逆元来实现

a b {\frac{a}{b}} ba%m = (a*inv(b))%m

2、如何求

  1. m为质数时费马小定理,m为非质数时欧拉定理。(Olog(n))
    总的可以归纳为欧拉定理求解,只不过对于欧拉定理特例费马小定理来说,m为质数可以直接得出答案,避免求欧拉函数一项省去一部分时间复杂度。

    费马小定理:
    当m为质数时,

    a m − 1 ≡ 1 ( % m ) {a^{m-1}\equiv 1 (\%m)} am11(%m)
    a ∗ a m − 2 ≡ 1 ( % m ) {a*a^{m-2}\equiv 1 (\%m)} aam21(%m)
    就可得:
    inv(a) = a m − 2 {a^{m-2}} am2%m

    欧拉定理:
    m不是质数

    a ϕ ( m ) ≡ 1 ( % m ) {a^{{\phi}{(m)}}\equiv 1 (\%m)} aϕ(m)1(%m)

    同理得:
    inv(a) ≡ a ϕ ( m ) − 1 { \equiv {a^{{\phi}{(m)}-1}}} aϕ(m)1%m

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0xffffff
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int mod=998244353;
const int N = 5e5+10;
int phi[N],vis[N],prime[N];
void get() //欧拉筛素数
{
    phi[1]=1;
    for(int i=2;i<N-10;i++)
    {
        if(!vis[i])
        {
            prime[++prime[0]]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=prime[0]&&prime[j]*i<N-10;j++)
        {
            vis[prime[j]*i]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}

int euler_phi(int n) 
{
    int m=sqrt(n+0.5);
    int ans=n;
    for(int i=2;i<=m;i++)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

ll qpow(ll a,ll b) //a^b快速幂
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a = a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

ll inv1(ll a) //费马小定理
{ return qpow(a, mod-2)%mod; }

ll inv2(ll a) //欧拉定理
{ return qpow(a, euler_phi(mod)-1); }

int main(int argc, char *argv[]) {
    get();
    int a;
    cin >> a;
    if(!vis[a]) cout << inv1(a);
    else cout << inv2(a);
}
  1. 拓展欧几里得 (Olog(n))
    设一个数为n,取模的数为m,gcd(n,m)==1。
    nx+my=gcd(n,m)=1
    两边同时对m取模

    nx ≡ {\equiv} 1(mod m)

    即x为n的逆元

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define inf 0xffffff
    using namespace std;
    //extern "C"{void *__dso_handle=0;}
    typedef long long ll;
    const int mod=998244353;
    const int N = 5e5+10;
    void exgcd(int a,int b,int& d,int& x,int& y)
    {
        if(!b) { d=a; x=1; y=0; }
        else { exgcd(b, a%b, d, y, x); y-=x*(a/b); }
    }
    
    int inv(int a)
    {
        int x,y,d;
        exgcd(a, mod, d, x, y);
        return (x+mod)%mod;
    }
    int main(int argc, char *argv[]) {
        int a;
        cin >> a;
        cout << inv(a);
    }
    
  2. 线性递推O(n)打表求[1,n]所有逆元
    求x%m的逆元
    m= ⌊ m x ⌋ ∗ x + m % x {\lfloor{\frac{m}{x}}\rfloor}*x+m\%x xmx+m%x
    ∵ m ≡ 0 ( % m ) {\because}m{\equiv} 0(\% m) m0(%m)
    ∴ ⌊ m x ⌋ ∗ x + m % x ≡ 0 ( % m ) {\therefore}{\lfloor{\frac{m}{x}}\rfloor}*x+m\%x{\equiv} 0(\% m) xmx+m%x0(%m)
    移项得
    ⌊ m x ⌋ ∗ x ≡ − m % x ( % m ) {\lfloor{\frac{m}{x}}\rfloor}*x{\equiv}-m\%x(\% m) xmxm%x(%m)
    两边同乘以inv(x)

    ⌊ m x ⌋ ∗ x ∗ i n v ( x ) ≡ ( − m % x ) ∗ i n v ( x ) ( % m ) {\lfloor{\frac{m}{x}}\rfloor}*x*inv(x){\equiv}(-m\%x)*inv(x)(\% m) xmxinv(x)(m%x)inv(x)(%m)
    ∵ x ∗ i n v ( x ) ≡ 1 % m o d {\because}{x*inv(x){\equiv}1\%mod} xinv(x)1%mod
    ∴ ⌊ m x ⌋ ≡ ( − m % x ) ∗ i n v ( x ) ( % m ) {\therefore}{{\lfloor{\frac{m}{x}}\rfloor}{\equiv}(-m\%x)*inv(x)(\% m)} xm(m%x)inv(x)(%m)
    移项得( − ⌊ m x ⌋ {-\lfloor{\frac{m}{x}}\rfloor} xm为负数不好操作,加上m无可厚非)
    m − ⌊ m x ⌋ ≡ ( m % x ) ∗ i n v ( x ) ( % m ) {{m-\lfloor{\frac{m}{x}}\rfloor}{\equiv}(m\%x)*inv(x)(\% m)} mxm(m%x)inv(x)(%m)
    两边同乘以inv(m%x)
    ( m − ⌊ m x ⌋ ) ∗ i n v ( m % x ) ≡ ( m % x ) ∗ i n v ( x ) ∗ i n v ( m % x ) ( % m ) {{(m-\lfloor{\frac{m}{x}}\rfloor)*inv(m\%x)}{\equiv}(m\%x)*inv(x)*inv(m\%x)(\% m)} (mxm)inv(m%x)(m%x)inv(x)inv(m%x)(%m)
    同理得
    i n v ( x ) ≡ ( m − ⌊ m x ⌋ ) ∗ i n v ( m % x ) ( % m ) {{inv(x){\equiv}(m-\lfloor{\frac{m}{x}}\rfloor)*inv(m\%x)}(\% m)} inv(x)(mxm)inv(m%x)(%m)

  • i n v ( x ) ≡ ( m − ⌊ m x ⌋ ) ∗ i n v ( m % x ) ( % m ) {{inv(x){\equiv}(m-\lfloor{\frac{m}{x}}\rfloor)*inv(m\%x)}(\% m)} inv(x)(mxm)inv(m%x)(%m)
    发现x>m%x,所以可通过递推进行求解。
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0xffffff
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int mod=998244353;
const int N = 5e5+10;
ll inv[N];

int main(int argc, char *argv[]) {
    inv[1]=1;
    for(int i=2;i<N-10;i++)
        inv[i]=((mod-mod/i)*(inv[mod%i])+mod)%mod;
    int a;
    cin >> a;
    cout << inv[a];
}

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