同余运算和乘法逆元相关

· · 个人记录

同余:符号为≡,代表的运算含义如下:

关于同余运算的重要定理:

关键记公式:a^φ(n)≡1 (mod n),a^(p-1)≡1 (mod p)

费马小定理的推论:对一个任意正整数a,其乘法逆元为a^(p-2)。

Because a\*a^(p-2)≡1 (mod p)

p.s.模p运算下,乘方的次数同样可以mod p。

几个重要的乘法逆元算法

  1. 扩展欧几里得(exgcd)算法

    • 原理:ax+by=gcd(a,b)一定有正整数解

    • 使用:解乘法逆元

    • 实现:逐层缩小a,b的范围,直到求出一个确切解。将确切解回代求出上层解直至完成初始解。

    • 复杂度:单次O(logn);

      int exgcd(int num_1,int num_2,int &sol_1,int &sol_2){
      if(num_2==0){
      sol_1=1,sol_2=0;
      return num_1;//底层求解 
      }
      printf("solve:%3dsol_1+%3dsol_2=gcd(%3d,%3d)\n",num_1,num_2,num_1,num_2);//输出中间值 
      int div=exgcd(num_2,num_1%num_2,sol_1,sol_2);//向下递归 
      //  cout<<" ans: "<<"x="<<x<<" y="<<y<<endl;
      int tmp=sol_1;sol_1=sol_2,sol_2=tmp-sol_2*(num_1/num_2);//顶层递归,退栈回代 
      printf("ans: sol_1=%3d sol_2=%3d\n",sol_1,sol_2);
      return div;//返回约数 
      }
  2. 费马小定理算法

    • 原理:a在mod p意义下的乘法逆元是a^p-2

    • 没了。胡搞硬算,小心溢出。

    • 复杂度:单次O(logn),但实际测试略慢于exgcd

    • 优先:相当简单

    inv[n]=__pow(n,p-2);
  1. 阶乘求解算法

    • 原理:

      • fac[i]=fac[i-1]*i;

      • inv[fac[i]]*i=inv[fac[i]];

      • 则可以边推算其阶乘的逆元,边利用两个阶乘的逆元计算中间一个数的逆元。

    • 适用范围:对多个连续数求解。

    • 复杂度:单次O(n),均摊O(1)

    • 以后可能会很有用

    fac[0]=1;
    for(register int i=1;i<=n;++i){
        fac[i]=((lint)fac[i-1]*i)%p;
    } 
    inv[n]=__pow(fac[n],p-2);//从n开始向前推
    for(register int i=n-1;i>=0;--i){
        res[i+1]=(lint)inv[i+1]*fac[i]%p;
        inv[i]=(lint)inv[i+1]*(i+1)%p;
    } 
  1. 递推求解算法

    • 思维难度高,适用范围窄,运行速度快,需要知识少。

    • 推理:

      • 设 p=k*i+r
      • 则 k*i+r≡0 (mod p)
      • 则 -k*i≡r (mod p)
      • 则 -k*inv[i]≡inv[r] (mod p)
      • 则 最终化简可得a[i]=-(p/i)*a[p%i];
    • 没了。

      inv[1]=1;puts("1");
      for(register int i=2;i<=n;++i){
      inv[i]=(lint)(p-p/i)*inv[p%i]%p;
      printf("%d\n",inv[i]);
      }