zoukankan      html  css  js  c++  java
  • 数论学习_逆元意义及求法

    首先说明逆元的概念,类似于倒数的性质。

    方程ax≡1(mod  p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。

    对于一些题目会要求把结果MOD一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,

    但对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD的,但是可以写为(a*b^-1)%MOD,其中b^-1表示b的逆元。

    知道了逆元的作用,接下来就是逆元的求法。

    首先,有一个费马小定理:

    费马小定理(Fermat's little theorem)数论中的一个重要定理,在1636年提出,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

    意思很明了,由上方的公式很容易推导出:a*a(p-2)≡1(mod p)对于整数a,p,a关于p的逆元就是a^(p-2),直接快速幂解之即可,但注意这个定理要求a,p互质!

    下面以HDU5685为例

    http://acm.hdu.edu.cn/showproblem.php?pid=5685

    题目给出的hash计算法就是连续区间元素的乘积对9973取模,我们自然而然想到前缀积的方法,但是数很大,前缀积显然只能存下%9973后得值,

    但有:(A/B)%M=(A%M*(B^-1%M))%M,A%M显然就是pre[A],B的逆对M取余就是qpow(B,M-2,M),快速幂模之即可。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define LL int
     4 #define MOD 9973
     5 LL inv[10000];
     6 LL pre[100005];
     7 char s[100005];
     8 LL qpow(LL a,LL b,LL M){
     9 LL r=1;
    10 while(b){
    11     if(b&1) r=r*a%M;
    12     b>>=1;
    13     a=a*a%M;
    14     }
    15     return r;
    16 }
    17 int main()
    18 {
    19     int N,i,j,k;
    20     inv[1] = 1;
    21     for(i=2;i<=9972;++i) inv[i]=qpow(i,MOD-2,MOD);
    22     while(scanf("%d",&N)==1){pre[0]=1;
    23         scanf("%s",s+1);
    24         int n=strlen(s+1);
    25         for(i=1;i<=n;++i){
    26             pre[i]=pre[i-1]*(s[i]-28)%MOD;
    27         }
    28         while(N--){int a,b;
    29             scanf("%d%d",&a,&b);
    30             printf("%d
    ",pre[b]*inv[pre[a-1]]%MOD);
    31         }
    32     }
    33     return 0;
    34 }

     还有一种线性递推的方法:

       inv[1] = 1;
        for(i=2;i<MOD;++i) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;  (isprime(MOD))

    对于这个式子的证明:

      M=k*i+r≡0   (mod M)

    式子两边同乘上 i-1*r-1,如果M不是质数得话r就可能为零

                  k*r-1+i-1≡0         (mod M)

                 i-1≡M-floor(M/i)*(M mod i)-1    (mod M)

    易证 <==> i-1=(M-M/i)*inv[M%i]%M;

    还有就是拓展欧几里得算法,对于一些很大的数,用递推法非常耗时,但又不适用于快速幂的时候(可能会爆long long 无法得出正确结果),

    考虑使用这个算法。

    我们对这个算法进行推演一下,exgcd求解的是一组   ax+by=gcd(a,b) 的一组(x,y)的解,

    我们设 

                     ax1+by1=gcd(a,b)-----------------------1  

                     bx2+(a%b)*y2=gcd(b,a%b)------------2       

    根据朴素的欧几里得算法可得 1,2式的右端是相等的,同理左端也相等,

             ax1+by1=bx2+(a%b)*y2

    ==>  ax1+by1=bx2+(a-a/b*b)*y2

    ==>ax1+by1=ay2+b(x2-a/b*y2)

    显然我们能得出  x1=y2;    y1=x2-a/b*y2;

    也就是说我们根据x2,y2便可推导出x1,y1的值,只要将a,b换成b,a%b即可,和gcd算法类似这是一个递归的过程,出口当b==0时,此时 x=1,y=0,gcd(a,b)=d=a;

    对于ax+by=gcd(a,b)这个式子,当gcd(a,b)=1的时候,ax+by=1,根据逆元的定义可知x的解就是a关于模b的逆元,y的解就是b关于模a的逆元,当gcd(a,b)=d=1时成立。

    (ax+by=1 -->  ax(mod b)=(1-by) (mod p)=1 (mod p) )

    这是gcd关于递推法的比较答案显然一致

     1 #include<iostream>
     2 using namespace std;
     3 #define LL long long
     4 #define mod 997
     5 void gcd(LL a,LL b,LL &d,LL &x,LL &y)
     6 {
     7     if(!b) {d=a;x=1;y=0;}
     8     else {gcd(b,a%b,d,y,x);y-=x*(a/b);}
     9 }
    10 LL finv(LL a,LL n)
    11 {
    12   LL d,x,y;
    13   gcd(a,n,d,x,y);
    14   return d==1?(x+n)%n:-1;
    15 }
    16 int main()
    17 {
    18     int inv[100]={1,1};
    19     for(int i=2;i<=99;++i){
    20         inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    21         cout<<inv[i]<<" "<<finv(i,mod)<<endl;
    22     }
    23     return 0;
    24 }

     对于逆元运算还有一些常见的式子,如:

                                                               1.(a^(-1))^(-1)=a;
                                                               2.(a*b)^(-1)=b^(-1)*a^(-1)

    对他们的证明:

    设单位元为e.
     x的逆元按定义是满足x·y = y·x = e的元素y.
    逆元是唯一的:设x·y = y·x = e,x·z = z·x = e,有y = y·e = y·(x·z) = (y·x)·z = e·z = z.
    1.a^(-1)是a的逆元,即有a·a^(-1) = a^(-1)·a = e,也即a^(-1)·a = a·a^(-1) = e.
    于是a也是a^(-1)的逆元,可写为(a^(-1))^(-1) = a.
    2.(a·b)·(b^(-1)·a^(-1)) = a·(b·(b^(-1))·a^(-1) = a·e·a^(-1) = a·a^(-1) = e.
    (b^(-1)·a^(-1))·(a·b) = b^(-1)·(a^(-1)·a)·b = b^(-1)·e·b = b^(-1)·b = e.
    于是b^(-1)·a^(-1)是a·b的逆元,即有b^(-1)·a^(-1) = (a·b)^(-1).

  • 相关阅读:
    Qt下如何修改文件的时间(全平台修改)
    Qt在windows 平台操作保存execel的表格(通过QAxObject来操作)
    VirtualTreeView控件
    VS2013设置release版本可调试
    工程脚本插件方案
    decode函数
    一个消息调度框架构建
    数据访问模式之Repository模式
    Angular.js Services
    OpenCascade简介
  • 原文地址:https://www.cnblogs.com/zzqc/p/7192436.html
Copyright © 2011-2022 走看看