zoukankan      html  css  js  c++  java
  • 乘法逆元详解

    1. 什么是乘法逆元?

    百度百科上是这样解释的:

    乘法逆元,是指数学领域群 G 中任意一个元素 a ,都在 G 中有唯一的逆元 a',具有性质 a×a'=a'×a=e,其中 e 为该群的单位元。

    不用看了,我知道你看不懂

    逆元其实很简单,

    (acdot xequiv1 pmod {p}),(一般 (p) 为质数)

    则称 (x)(a)(p) 的逆元,记为 (operatorname{inv}(x)=a)


    2. 乘法逆元能干嘛?

    逆元一般用来求形如 (frac{a}{b} mod p) 的式子的值,

    (k)(b)(p) 的逆元,则有 (frac{a}{b} mod p=akmod p)

    下面来看看这个式子是怎么推导的。

    (k)(b)(p) 的逆元,即 (bkmod p=1),有:

    [egin{align*} frac{a}{b}mod p &=left(frac{a}{b}mod p ight)cdot 1\ &=left(frac{a}{b}mod p ight)left(bkmod p ight)\ &=left(frac{a}{b}cdot bk ight)mod p\ &= akmod p end{align*} ]


    3. 逆元怎么求?

    (1) exgcd

    还记得这个式子吗?(axequiv 1 pmod {p})

    这个式子可以转化为 (ax+py={1}),((yinmathbb{Z})

    这个时候就可以用 exgcd 来求逆元,

    时间复杂度大约为 (mathcal O(log n))

    #include<bits/stdc++.h>
    using namespace std;
    void exgcd(int &x,int &y,int a,int b)
    {
        if(!b)
        {
            x=1,y=0;
            return;
        }
        exgcd(x,y,b,a%b);
        int t=x;
        x=y;
        y=t-a/b*y;
    }
    int main()
    {
        int n,p;//求n关于p的逆元
        scanf("%d %d",&n,&p);
        int x=0,y=0;
        exgcd(x,y,n,p);
        printf("%d",(x%p+p)%p);
        return 0;
    }
    

    (2) 费马小定理

    费马小定理:若 (p) 为素数,(ainmathbb{Z^+}),且 (a)(p) 互质,则有 (a^{p-1}equiv 1(mod{p}))

    这时发现上式和式子 (axequiv 1 pmod {p}) 的右边部分都为 (1)

    所以,(axequiv a^{p-1}pmod p)

    化简得 (xequiv a^{p-2} pmod p)

    如果 (a^{p-2}) 用快速幂计算,时间复杂度将和 exgcd 同级,为 (mathcal O(log n))

    code:

    #include<bits/stdc++.h>
    using namespace std;
    int FastPow(int a,int n,int mod)
    {
        int base=a,ans=1;
        while(n)
        {
            if(n&1) ans=(base*ans)%mod;
            base=(base*base)%mod;
            n>>=1;
        }
        return ans;
    }
    int main()
    {
        int n,p;//求n关于p的逆元
        scanf("%d %d",&n,&p);
        printf("%d",FastPow(n,p-2,p));
        return 0;
    }
    

    (3) 逆元的线性递推式

    此处参考 https://zhuanlan.zhihu.com/p/100587745?utm_source=qq

    例如洛谷P3811,要求求区间 ([1,n]) 的所有数的逆元,这个时候, exgcd 和费马小定理都显得有些“笨”。于是,就有了线性递推式。

    假设我们现在想求 (a) 的逆元,为了方便之后的推导,我们将 (p) 表示成这样的形式: (p=aq+r)

    此处,(q=lfloor p/a floor,r=pmod a)

    容易看出,(aq+requiv 0pmod p)

    移项整理可得 (aequiv -rcdot operatorname{inv}(q)pmod p)

    两边同时取倒数,得出 (operatorname{inv}(a)equiv -qcdot operatorname{inv}(r)pmod p)

    再将 (q=lfloor p/a floor,r=pmod a) 代入,我们就得到了逆元的递推式 (operatorname{inv}(a)equiv -lfloor p/a floorcdot operatorname{inv}(pmod a)pmod p)

    我们发现,式子中的 (-lfloor p/a floor) 有可能是负数,所以我们给它加一个 (p)

    所以,最终的递推式为:

    [operatorname{inv}(a)equiv (p-lfloor p/a floor)cdot operatorname{inv}(pmod a)pmod p ]

    真正实现的时候,将 (equiv) 当作 (=) 即可。

    然后就可以实现 (mathcal O(n)) 递推啦。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10000010;
    int n,p,inv[N];
    int main()
    {
        scanf("%d%d",&n,&p);
        inv[1]=1;
        for(int i=2;i<=n;i++)
            inv[i]=(p-p/i)*inv[p%i]%p;
        printf("%d",inv[n]);
        return 0;
    }
    

    4.参考资料

    https://blog.csdn.net/yu121380/article/details/81188274

    https://www.luogu.com.cn/blog/zjp-shadow/cheng-fa-ni-yuan

    https://www.cnblogs.com/song-/p/9724555.html

    https://zhuanlan.zhihu.com/p/100587745?utm_source=qq

  • 相关阅读:
    Java后端WebSocket的Tomcat实现
    Swift学习资源
    jwplayer 隐藏属性方法记载
    简单的java socket 示例
    SQL SERVER 2008/2012/2012R2/2014 设置开启远程连接(sa配置)
    JS原型继承和类式继承
    理解js中的原型链,prototype与__proto__的关系
    C# 事件(Event)
    Java 与 JavaScript 对websocket的使用
    C# 版dll 程序集合并工具
  • 原文地址:https://www.cnblogs.com/juruo-zzt/p/niyuan.html
Copyright © 2011-2022 走看看