数论 乘法逆元

乘法逆元

定义

\(ax\equiv 1\pmod p\) 成立,则称 \(x\)\(a\) 在模 \(p\) 意义下的逆元,同时 \(a\) 也是 \(x\) 在模 \(p\) 意义下的逆元

\(\because\ 上述等式成立,当且仅当a\perp p,且x\perp\ p\)

\(\therefore\ 若不互质,则逆元不存在\)

特殊地, \(0\) 没有乘法逆元, \(1\) 的乘法逆元是 \(1\) 本身(与 \(p\) 无关)

一般将一个数 \(a\) 在模 \(p\) 的逆元记为 \(a^{-1}\pmod p\)

意义

  • 在模运算中,有以下公式成立:

    \[\begin{align} (a+b)\%m&=((a\%m)+(b\%m))\%m\tag{1}\\ (a-b)\%m&=((a\%m)-(b\%m))\%m\tag{2}\\ (a\times b)\%m&=((a\%m)\times(b\%m))\%m\tag{3} \end{align} \]

  • 在上面的公式中没有除法,即除法是不满足这个性质的

  • 乘法逆元的意义在于,把除以一个数变成乘上它的逆元,在模意义下结果不变

  • 这样就避免了大数字运算

证明:除以一个数变为乘上其逆元,在模意义下结果不变

\[\begin{align} \notag证&明:\frac ab\%p=a\times b^{-1}\%p\\\notag &设\ c=b^{-1}\pmod p\\\notag &有\ b\times c\equiv 1\pmod p\\\notag &\Rightarrow\ b\times c\%p=1\\\notag &\therefore\ \frac ab\%p=\frac ab\times1\%p\\\notag &\ \ \ \ \ \ =\frac ab\times b\times c\%p\\\notag &\ \ \ \ \ \ =a\times c\%p\\\notag &\ \ \ \ \ \ =a\times b^{-1}\%p\\\notag \therefore&\ 得证 \end{align} \]

性质

同余的相关性质
  1. 反身性: \(a\equiv a\pmod p\)
  2. 对称性: \(a\equiv b\pmod p\Rightarrow b\equiv a\pmod p\)
  3. 传递性: \(a\equiv b\pmod p,b\equiv c\pmod p\Rightarrow a\equiv c\pmod p\)
  4. 同余式相加减: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow\sum\limits_{i=1}^na_i\equiv\sum\limits_{i=1}^nb_i\pmod p\)
  5. 同余式相乘: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow\prod\limits_{i=1}^na_i\equiv\prod\limits_{i=1}^nb_i\pmod p\)
    • 推论1: \(\forall\ k\in\N^+,a_k\equiv b_k\pmod p\Rightarrow a^n\equiv b^n\pmod p\)
  6. \(a\equiv b\pmod p\Rightarrow a\times k\equiv b\times k\pmod {p\times k}\)
  7. \(a\equiv b\pmod p,d\ |\ m,d\in\N^+\Rightarrow a\equiv b\pmod d\)
  • 推论: \(\forall i\in\N^+,a\equiv b\pmod{m_i}\Rightarrow a\equiv b\pmod{\text{lcm}(m_1,m_2,\dots,m_n)}\)
  1. \(a\equiv b\pmod p\Rightarrow \gcd(a,m)=\gcd(b,m)\)
  2. \(a\times c\equiv b\times c\pmod p\Rightarrow a\equiv b\pmod{\frac p{\gcd(p,c)}}\)
  • 推论: \(a\times c\equiv b\times c\pmod p,\gcd(c,p)=1\Rightarrow a\equiv b\pmod p\)

求法

exGcd 扩展欧几里得

\[\begin{align} \notag&\because\ ax\equiv1\pmod p\ 可以写为:\\\notag &\ \ \ \ \ ax+py=1\tag{1}\\\notag &又\because a\perp p\Rightarrow \gcd(a,p)=1\\\notag &\therefore\ 方程(1)可以写作:\\\notag &\ \ \ \ \ ax+py=\gcd(a,p)\tag{2}\\\notag &根据裴蜀定理,方程(2)显然有解\\\notag &所以只需要解出\ x,就得到了\ a^{-1} \end{align} \]

用法 \(O(\log(a))\)

template<class t>inline t gcd(t a,t b,t &x,t &y){
    if(b==0){
        x=1,y=0;return;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
inline int sol(int a,int p){
    int x,y;//x is a's Inverse
    if(gcd(a,p,x,y)!=1){//use exGcd to get the solution
        return -1;//if a!⊥p then there's no solution
    }else{
        return (x%p+p)%p;
        //explain: if the 'x' we got is a negative number
        //then (x%p+p)%p can let it become a positive number
        //and in fact, if x≡a^-1 (mod p)
        //then x+p≡a^-1 (mod p)
    }   
}
欧拉函数求逆元

欧拉定理: \(a\perp p\Rightarrow a^{\varphi(p)}\equiv1\pmod p\)

上式可变形为 \(a\times a^{\varphi(p)-1}\equiv1\pmod p\)

由于 \(a\perp p\) ,所以 \(a^{-1}\equiv a^{\varphi(p)-1}\pmod p\)

用法 \(O(\log(p))\)

template<class t>t fPow(t x,t y,t p){
    //fast Power to get x^y%p
    t ans=1;
    for(;y;y>>=1){
        if(y&1)ans=ans*x%p;
        x=x*x%p
    }
    return ans;
}
template<class t>t phi(t n){
    t res=n;
    for(t i=2;i*i<=n;i++){
        //公式 phi(n)=n*1/i*(i-1)
        if(n%i==0)res=res/i*(i-1);
        while(n%i==0)n/=i;
    }
    if(n>1)res=res/n*(n-1);
    return res;
}
inline int sol(int a,int p){
	return fPow(a,phi(p)-1,p);
}
筛法求逆元

上述的算法在求单个数的时候表现都不错,但是求多个数的时候效率会较为低下,这时候就需要筛法出场了

\[\begin{align} \notag &设\ x_i为a_i的逆元\\\notag &\because\ x_i=a_i^{-1}\pmod p\\\notag &\therefore\ {a_n}的逆元可以表示为 \left\{a_n^{-1}\right\}\\\notag &设\ \{s_n\}中\ s_n=\prod_{k=1}^nk\%p\\\notag &\therefore\ s_i=(s_{i-1}\times a_i)\%p\tag{1}\\\notag &\therefore\ s_i^{-1}=(s_{i-1}^{-1}\times a_i^{-1})\%p\\\notag &\therefore\ s_{i-1}^{-1}=(s_i^{-1}\times a_i)\%p\tag{2}\\\notag &\therefore\ a_i^{-1}=(s_{i-1}\times s_i^{-1})\%p\tag{3} \end{align} \]

用法 \(O(n+\log(a_n))\) (欧几里得函数求逆元) 或 \(O(n+\log(p))\) (欧拉函数求逆元)

//欧几里得函数求逆元
typedef long long lld;
const int maxn=5e6+12;
lld p;
int n;
lld a[maxn],ina[maxn],s[maxn],ins[maxn];
//a[i] is the number the input gives
//ina[i] is a[i]^-1 mod p
//s[i] is a[1]*a[2]*...*a[i]
//ins[i] is s[i]^-1 mod p
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1;y=0;return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
inline void sol(){
    s[0]=1;//ini
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        s[i]=(s[i-1]*a[i])%p;//according to (1)
    }
    lld x,y;
    gcd(s[n],p,x,y);
    x=(x%p+p)%p;//to make sure x is between (0,p)
    ins[n]=x;
    for(int i=n;i>0;i--){
        ins[i-1]=(ins[i]*a[i])%p;//according to (2)
        ina[i]=(s[i-1]*ins[i])%p;//according to (3)
    }
}
//欧拉函数求逆元同理

例题

有理数取模

给出一个有理数 \(c=\frac ab\) ,求 \(c\mod 19260817\)

\(0\le a,b\le 10^{10001}\)

\(p=19260817\)

因为 \(\frac ab=a\times b^{-1}\pmod p\)

所以我们要求的就是 \(a\times b^{-1}\mod p\)

因为数据很大,考虑字符处理

\(\{a_n\}\)\(a_i=x的第i位数(从左至右)\)

在十进制下: \(x=(\dots((a_1\times10+a_2)\times10+a_3)\times 10+\dots)\times 10+a_{n-1})\times 10+a_n\)

所以 \(x\%p=(\dots(a_1\%p)\times10+(a_2\%p))\dots\)

做法:

#include<bits/stdc++.h>
using namespace std;
const long long p=19260817;
#define GO(u,v,i) for(register int i=u;i<=v;i++)
template<class t>inline t fr(){
    register t num=0,dis=1;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')dis=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){num=((num<<1)+(num<<3))%p+(ch^48);ch=getchar();}
    return num*dis%p;
}
template<class t>inline void fw(t num){
    if(num>9)fw(num/10);
    putchar(num%10+'0');
}
template<class t>inline void fw(t num,char ch){
    if(num<0)num=-num,putchar('-');
    fw(num);putchar(ch);
}
typedef long long lld;
lld a,b;
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1,y=0;return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
signed main(){
    a=fr<lld>();
    b=fr<lld>();
    lld x,y;
    if(gcd(b,p,x,y)!=1){//no soluion
        printf("Angry!");//as the question requires
    }else
    fw((a*(((x%p)+p)%p))%p,'\n');
    return 0;
}
乘法逆元

给定 \(n\) 个正整数 \(a_i\)

\(p=10^9+7,k=998244353\)

\(\sum\limits_{i=1}^n(a_i^{-1}\times k^{n-i})\pmod p\)

证明过程在筛法求逆元

#include<bits/stdc++.h>
using namespace std;
#define GO(u,v,i) for(int i=u;i<=v;i++)
template<class t>inline t fr(){
    register t num=0,dis=1;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')dis=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
    return num*dis;
}
template<class t>inline void fw(t num){
    if(num>9)fw(num/10);
    putchar(num%10+'0');
}
template<class t>inline void fw(t num,char ch){
    if(num<0)num=-num,putchar('-');
    fw(num);putchar(ch);
}
const int maxn=5e6+12;
typedef long long lld;
const lld p=1e9+7,k=998244353;
int n;
lld a[maxn],s[maxn],sns[maxn],an[maxn];
lld anss;
template<class t>inline t gcd(t a,t b,t&x,t&y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    t g=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return g;
}
template<class t>inline t power(t num,t tim){
    t ans=1;
    GO(1,tim,i)ans*=num;
    return ans;
}
inline void sol(){
    n=fr<int>();
    s[0]=1;//初始化
    GO(1,n,i){
        a[i]=fr<lld>();
        s[i]=(s[i-1]*a[i])%p;//根据公式(1)
    }
    lld x,y;
    lld g=gcd(s[n],p,x,y);
    lld ans=x,t=0;
    while((ans=x+p/g*t)<1)t++;
    while((ans=x+p/g*t)>=p)t--;
    sns[n]=ans;// 1/bn 已求出
    lld tmp=1;
    for(int i=n;i>0;i--){
        sns[i-1]=(sns[i]*a[i])%p;//根据公式(2)
        an[i]=(sns[i]*s[i-1])%p;//根据公式(3)
        anss+=(an[i]*tmp)%p;//题目要求
        tmp*=k;tmp%=p;//tmp就是k^(n-i)
    }
    fw(anss%p,'\n');
}
signed main(){
    sol();
    return 0;
}
posted @ 2022-06-01 21:47  Locked_Fog  阅读(145)  评论(0)    收藏  举报