• 逆元的三种解法(附详细证明)


    友情提示:

    Latex加载稍慢,请耐心等待

    什么是逆元?

    若$x$满足

    $a*xequiv 1(mod p)$

    我们称$x$是$a$在$mod p$意义下的逆元

     

    逆元的基本解法

    https://loj.ac/problem/110

    1.快速幂

    当p为素数

    根据费马小定理

    $a^{(p-1)}equiv 1(mod p)$

    ${color{Green}a*a^{(p-2)}equiv 1(mod p) }$

     

    带入快速幂就好啦

    时间复杂度:$O(log_2^p)$

     1 #include<cstdio>
     2 #define LL long long 
     3 using namespace std;
     4 const LL MAXN=200000001;
     5 LL n,mod;
     6 LL fastpow(LL val,LL p)
     7 {
     8     LL base=1;
     9     while(p)
    10     {
    11         if(p&1)    base=(base*val)%mod;
    12         val=(val*val)%mod;
    13         p>>=1;
    14     }
    15     return base;
    16 }
    17 int main()
    18 {
    19     scanf("%lld%lld",&n,&mod);
    20     for(LL i=1;i<=n;i++)
    21         printf("%lld
    ",fastpow(i,mod-2)%mod);
    22     return 0;
    23 }

    2.扩展欧几里得

    对于$a*xequiv 1(mod p)$

    他的另一种写法为

    $a*x+p*y=1$(想一想,为什么)

    扩展欧几里得,带入求解

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int n,mod;
     6 inline int read()
     7 {
     8     char c=getchar();int  flag=1,x=0;
     9     while(c<'0'||c>'9')    {if(c=='-')    flag=-1;c=getchar();}
    10     while(c>='0'&&c<='9')    x=x*10+c-48,c=getchar();    return x*flag;
    11 }
    12 int x,y;
    13 int gcd(int a,int b)
    14 {
    15     return b==0?a:gcd(b,a%b);
    16 }
    17 int exgcd(int a,int b,int &x,int &y)
    18 {
    19     if(b==0)
    20     {
    21         x=1,y=0;
    22         return a;
    23     }
    24     int r=exgcd(b,a%b,x,y);
    25     int tmp=x;x=y;y=tmp-(a/b)*y;
    26     return r;
    27 }
    28 int main()
    29 {
    30     n=read(),mod=read();
    31     for(int i=1;i<=n;i++)
    32     {
    33         int g=exgcd(i,mod,x,y);
    34         while(x<0)    x+=mod;
    35         printf("%d
    ",x);
    36     }
    37     return 0;
    38 }

    时间复杂度:$O(log_2^n)$

     3.递推

    前两种方法常用来求单个逆元

    对于逆元的需要量比较大的时候,我们可以使用递推的方法来求逆元

    前提条件:$P$为素数

    推导过程

    设$t=P/i$
    $k=P mod i$

    显然有

    $t*i+k equiv  0 (mod P)$

    $k equiv -t*i(mod P)$

    两侧同除$i*k$,把$t$和$k$带入

    $inv[i] equiv -p/i*inv[p mod i] (mod p)$

    这里需要注意一个事情,

    对于 $amod p$当$a<0$时,

    应为$(a+p) mod p$

    这样就可以把原式的$mod p$消掉,得

    $inv[i]=P-P/i*inv[Pmod i]$

    这样就可以进行线性的递推啦

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #define LL unsigned long long 
     6 using namespace std;
     7 const LL MAXN=200000001;
     8 inline LL read()
     9 {
    10     char c=getchar();LL flag=1,x=0;
    11     while(c<'0'||c>'9')    {if(c=='-')    flag=-1;c=getchar();}
    12     while(c>='0'&&c<='9')    x=x*10+c-48,c=getchar();    return x*flag;
    13 }
    14 LL inv[MAXN];
    15 LL n,p;
    16 int main()
    17 {
    18     n=read(),p=read();
    19     inv[1]=1;
    20     printf("1
    ");
    21     for(int i=2;i<=n;i++)
    22     {
    23         inv[i]=(p-p/i)*inv[p%i]%p;
    24         printf("%d
    ",inv[i]);
    25     }
    26     return 0;
    27 }

    时间复杂度:$O(n)$

    总结

    在求多个数的逆元的时候,推荐使用递推算法

    在求单个数的逆元的时候,推荐使用扩展欧几里得算法

    因为扩展欧几里得算法不受模数的限制,而且自测运行效率比快速幂高不少

  • 相关阅读:
    盾机
    隐藏服务器真实IP的方法来防止DDOS攻击
    EJS 是什么 ,怎么用,以及优点
    Centos7 / RHEL 7 双网卡绑定
    linux下配置双网卡及RAC规划——1
    Why React Is Favored by Front-End Specialists
    React Virtual DOM Explained in Simple English
    React Core Features
    Virtual DOM--react
    redux是全局状态(数据)的管理机制,局部数据没有意义
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/7730375.html
Copyright © 2020-2023  润新知