[笔记] 浅谈乘法逆元

一.定义

(及如何理解)

如果a*x≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x。(from Wikipedia)

a*x≡1 (mod p) 表示 a乘一个数x并模p等于1,即 a*x%p=1;看上去就是同余定理的一个简单等式。

而x 为 a 的逆元,记为x=a-1,所以我们也可以称 x 为 a 在mod b意义下的倒数(定义了剩余系中的除法),

什么意思呢? 可以理解为在x的倒数上加了个限定:

倒数定义为a*x=1,则x为a的倒数;

而逆元为:a*x%p=1;

 

用处:一般用于求 a/b (mod p) 的值(p 通常为质数),是解决模意义下分数数值的必要手段。

    (比如某道水题让你对结果%p,而计算过程中有除法运算。。)

二.求解

1.扩展欧几里得算法求解逆元

  我们知道,a*x≡1 (mod p) 就是 a*x%p=1 也就是 a*x+p*y=1  

  而扩展欧几里得算法就是用来求线性同余方程axmod ),只不过此时c=1;

  

#include<cstdio>
#include<cmath>
#define R register int
#define ll long long
using namespace std;
int n,p;

void ex_gcd(ll a,ll b,ll& x,ll& y)
{
    if(!b) x=1,y=0;
    else ex_gcd(b,a%b,y,x),y-=a/b*x;
}

int main()
{
    scanf("%d%d",&n,&p);//求1到n在mod p意义下的逆元
    for(R i=1;i<=n;i++)
    {
        ll x,y;
        ex_gcd(a,p,x,y);
        x=(x%p+p)%p;
        printf("%d\n",x);
    }
    return 0;
}

 

2.费马小定理和快速幂求解逆元(此时p为质数)

由费马小定理可知:ap-1≡1 (mod p)

所以a*ap-2≡1 (mod p)

即ap-2就是a在mod p意义下的逆元

#include<cstdio>
#include<iostream>
#include<cmath>
#define R register int 
#define ll long long 
using namespace std;

int n,p;

ll q_pow(ll x,ll ind,ll mod)
{
    x%=mod;
    ll a=1;
    for(;ind;ind>>=1,(x*=x)%=mod)
        if(ind&1) (a*=x)%=mod;
    return a;
}

int main()
{
    scanf("%d%d",&n,&p);//求1到n在mod p意义下的逆元
    for(R i=1;i<=n;i++) printf("%lld\n",q_pow(i,p-2,p));
    return 0;
}

若p为合数,则需要用到欧拉函数和欧拉定理(详见https://www.cnblogs.com/Jackpei/p/10372392.html)

#include<iostream>
#include<cstdio>
#include<cctype>
#define R register int
#define ll long long
using namespace std;
static ll a,p;

inline ll q_pow(ll x,ll ind,ll mod)
{
    x%=mod;
    register ll a=1;
    for(;ind;ind>>=1,(x*=x)%=mod) if(ind&1) (a*=x)%=mod;
    return a;
}

inline int phi(int n) 
{
    R ans=1;
    for(R i=2;i*i<=n;++i) if(n%i==0)
    {
        n/=i;
        ans*=i-1;
        while(n%i==0) n/=i,ans*=i;
    }
    if(n>1) ans*=n-1;
    return ans;
}

int main()
{
    scanf("%lld%lld",&a,&p);//求a在mod p 意义下的逆元
    printf("%lld\n",q_pow(a,phi(p)-1,p));
    return 0;
}

 

3.O(n)的线性算法

  原题:P3811 【模板】乘法逆元 https://www.luogu.org/problemnew/show/P3811

  求 [1,n] 区间中每个数的逆元,其实这是一种递推。。。

  我们设 inv[i] 表示i的逆元,设 p=k*i+r (1<r<i<p) 可得:k*i+r≡0 (mod p)  (显然)

  则左右同乘 r-1 * i-1 :k*r-1+i-1≡0 (mod p)

  所以:i-1≡ -k*r-1 (mod p);

  易知:r-1=inv[p%i],k=p/i

  所以:i-1 = (-p/i+p) * inv[p%i]%p

  而由逆元定义可知:inv[1]=1 (mod p),你就可以开始递推了。

  

#include<cstdio>
#include<iostream>
#define R register int 
using namespace std;
int n,p;
int inv[3000010];

int main()
{
    scanf("%d%d",&n,&p);//求1到n在mod p意义下的逆元
    inv[1]=1;printf("1\n");
    if(p>=2) for(R i=2;i<=n;i++) inv[i]=(long long)(p-p/i)*inv[p%i]%p,printf("%d\n",inv[i]);//话说记得用long long寄存,要不就死了。。。
    return 0;
}

 

4.阶乘逆元

。。。还未学会。。。安利一发zjp_shadow大佬的blog:https://www.cnblogs.com/zjp-shadow/p/7773566.html (写得太好了)

 

如有错误,恳请您指正(我太菜了);如有不理解,可留言,我会尽量回复。。。(高中生(逃)。。)

by Jackpei 2019.2.10

posted @ 2019-02-10 00:49  LuitaryiJack  阅读(698)  评论(2编辑  收藏  举报