zoukankan      html  css  js  c++  java
  • 逆元小结

    乘法逆元小结

    乘法逆元:在模意义下 乘法和加法都是合法的,但除法并不合法,为了解决除法的问题,引进了乘法逆元 inv(a),记作 a-1  ,即像满足 a*inv(a) ≡ 1 (mod p)这样的 a 的倒数的数 inv(a) 的存在。

    处理逆元的几种方法:

    1、暴力求解

    复杂度:O( p ) p 为 模。

    假设要求 x 在 mod p 情况下的逆元,暴力枚举 p-1 个数,满足条件:x * num ≡ 1 (mod p);

    2、费马小定理 && 欧拉定理

    复杂度:O(log2N)

    费马小定理:a(p-1) ≡ 1 (mod p) 【前提 p为质数】

    可得 inv(a) = a(p-2)

    欧拉定理:aphi( p ) ≡ 1 (mod p) 【前提 p不为质数】当然 p 不为质数的情况下不一定每一个数都存在逆元。

    可得 inv(a) = aphi(p)-1

    即根据 p 的情况,选用快速幂求解。

     1 LL pow_mod(LL a, LL b, LL p){            //a的b次方结果模p 
     2     LL ret = 1;
     3     while(b){
     4         if(b & 1) ret = (ret * a) % p;
     5         a = (a * a) % p;
     6         b >>= 1;
     7     }
     8     return ret;
     9 }
    10 LL Fermat(LL a, LL p){                //费马求a关于p的逆元 
    11         return pow_mod(a, p-2, p);
    12 }

     3、拓展欧几里得(by 《挑战》)

    由于方程 a * inv(a) ≡ 1 (mod p) 等价于存在一个整数 k 使得 a*inv(a) = 1+k*p;

    即问题转换成 求解满足 a* inv(a) - k*p = 1 的 inv(a) 的问题,可以运用 extgcd (拓展欧几里得)求解。

    同时!!! 如果 gcd(a, p) != 1 则逆元不存在。

     1 int exgcd(int a,int b,int &x,int &y) 
     2 {  
     3     if(b==0)
     4     {
     5         x=1;y=0;
     6         return a;  
     7     }
     8     int d=exgcd(b,a%b,y,x);
     9     y=y-a/b*x;
    10     return d;
    11 }
    12 int mod_inverse(int a,int mod)//求a关于mod的逆元
    13 {
    14     int x,y;
    15     exgcd(a,mod,x,y);
    16     return (x%mod+mod)%mod;
    17 } 

    4、万能公式法

    以上几种方法都存在局限性,那么怎么办呢?

    在 a | b 的情况下: a/b mod p = a mod (bp) / b 

    5、神奇线性大法预处理 1~N 的逆元

    参考:http://blog.miskcoo.com/2014/09/linear-find-all-invert

    模板:

    inv[1] = 1;

    inv [ i ] = (mod - mod/i)*inv[ mod%i ] %mod; 

    6、巧妙预处理阶乘逆元

    在HDU多校一道莫队的题看到的技巧,当时就ORZ.....

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

    首先快速幂求出 (maxn-1) ! 在mod的意义下的逆元;

    逆推: inv[ i ] = (inv[ i + 1]*(i+1))%mod;

     1 void init()
     2 {
     3     rev2 = q_pow(2, MOD-2);                     // 2的逆元
     4     fac[0] = fac[1] = 1;
     5     for(LL i = 2; i < MAXN; i++){              //预处理阶乘
     6         fac[i] = fac[i-1]*i%MOD;
     7     }
     8 
     9     inv[MAXN-1] = q_pow(fac[MAXN-1], MOD-2);    //逆推预处理阶乘的逆元
    10     for(int i = MAXN-2; i >= 0; i--){
    11         inv[i] = inv[i+1]*(i+1)%MOD;
    12     }
    13 }
    1 void Init()                                 //预处理排列数和逆元
    2 {
    3     fac[0] = Inv[0] = fac[1] = Inv[1] = 1;
    4     for(int i = 2; i < MAXN; i++) fac[i] = fac[i-1]*i%mod;
    5     for(int i = 2; i < MAXN; i++) Inv[i] = (mod-mod/i)*Inv[mod%i]%mod;
    6     for(int i = 2; i < MAXN; i++) Inv[i] = Inv[i]*Inv[i-1]%mod;
    7 }
  • 相关阅读:
    编程开发之--单例模式(2)
    编程开发之--单例模式(1)
    oracle 存储过程
    数据结构与算法之--最大公约数、最小公倍数
    编程开发之--Oracle数据库--存储过程使用动态参数绑定(3)
    软件开发之常用的工具
    Oracle PL/SQL学习之你需要知道的快捷键
    Linux下安装Tomcat服务器和部署Web应用
    如何在linux下安装tomcat服务器
    CentOS7 64位安装mysql教程
  • 原文地址:https://www.cnblogs.com/ymzjj/p/10330475.html
Copyright © 2011-2022 走看看