• [笔记] 浅谈乘法逆元


    一.定义

    (及如何理解)

    如果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
    ",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
    ",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
    ",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
    ");
        if(p>=2) for(R i=2;i<=n;i++) inv[i]=(long long)(p-p/i)*inv[p%i]%p,printf("%d
    ",inv[i]);//话说记得用long long寄存,要不就死了。。。
        return 0;
    }

    4.阶乘逆元

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

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

    by Jackpei 2019.2.10

  • 相关阅读:
    微信小程序实现运动步数排行(可删除)
    一个文艺的在线生成漂亮的二维码工具网站
    微信小程序常见的UI框架/组件库总结
    小程序踩坑记- tabBar.list[3].selectedIconPath 大小超过 40kb
    推荐一款便捷的在线图片处理工具
    如何在本地运行查看github上的开源项目
    微信小程序实现运动步数排行(可删除)
    从零开始学ios开发(三):第一个有交互的app
    从零开始学ios开发(二):Hello World!来啦!
    从零开始学ios开发(一):准备起航
  • 原文地址:https://www.cnblogs.com/Jackpei/p/10358563.html
Copyright © 2020-2023  润新知