zoukankan      html  css  js  c++  java
  • 逆元Inv(模板+应用)

    逆元:

    如果满足公式a*b\equiv 1(mod m),则有a 是 b的逆元同时b也是a的逆元。

    逆元的应用:

    设c为b在对m取余的意义下的逆元;

    在求解公式 (a / b) % m的时候,如果b可能会非常的大,所以会出现爆精度的问题,这个时候就需要将除法转换成乘法来做,即:

    (a / b )  % m = (a * c)%m。

    逆元的求法:

    一、扩展欧几里得求逆元

    复杂度:O(logn)(实际就是斐波那契数列)

    将公式(b、p已知)   a∗b≡1(mod p)   转换为   a∗b+k∗p=1  则有a为b对p取余意义下的逆元,且只有当a与p互质是逆元才存在

    注意:只要存在逆元就可以求,适用于逆元个数不多,但是mod很大的时候。

    附一个百度百科的例子加深一下理解:

    代码:

    /*
        Time:2018/8/31
        Writer:Sykai
        Function:利用扩展欧几里得求逆元
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <set>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 100;
    const int MOD = 1e9 + 7;
    typedef long long ll;
    typedef pair<int,int> P;
    
    //公式a∗b+k∗p=1中a即是a,p即是b
    ll exgcd(ll a,ll b,ll& x,ll& y)
    {
        if(b == 0)
        {
            x = 1;
            y = 0;
            return a;
        }
        ll res = exgcd(b, a%b, y, x);
        y -= a/b*x;
        return res;
    }
    //公式a∗b+k∗p=1中a即是a,p即是mod
    ll getInv(int a,int mod)//求a在mod下的逆元。如果不存在就返回-1
    {
        ll x,y;
        ll res = exgcd(a,mod,x,y);
        return res = 1 ? (x%mod + mod)%mod:-1;//return res = 1?(x + mod)%mod : -1;
    }
    
    int main()
    {
        ll a = 5;
        printf("%lld\n",getInv(a,MOD));
        return 0;
    }

    二、费马小定理求逆元

    复杂度:O(logn)

    费马小定理: 假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)。

    a*a^(p-2) ≡ 1 (mod p),则有a^(p-2)是a的对p取余时候的逆元

    注意:当p是素数的时候一般选用费马小定理来求逆元。

    代码:

    /*
        Time:2018/8/31
        Writer:Sykai
        Function:利用费马小定理求逆元
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <set>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 100;
    const int MOD = 1e9 + 7;
    typedef long long ll;
    typedef pair<int,int> P;
    
    ll qpow(ll a,ll b)//求a*a^(p-2) ≡ 1 (mod p)中a^(p-2)
    {
        ll res = 1;
        while(b)
        {
            if(b&1)
                res = res * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return res;
    }
    
    ll getInv(ll a,ll mod)
    {
        return qpow(a,mod-2);
    }
    
    int main()
    {
        int a = 5;
        printf("%lld\n",getInv(a,MOD));
        return 0;
    }

    三、递推求逆元

    复杂度:O(n)

    注意:

    1、mod需要是质数,求得是1~N关于mod的逆元。

    2、适用于mod不是太大,且被多次调用。

    3、程序开始前需要预处理打表。

    代码:

    /*
        Time:2018/8/31
        Writer:Sykai
        Function:线性求逆元
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <set>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 100;
    const int MOD = 1e9 + 7;
    typedef long long ll;
    typedef pair<int,int> P;
    ll inv[maxn];//数组的大小需要根据实际情况来调整
    
    void getInv()
    {
        inv[1] = 1;
        for(int i = 2; i < maxn; i++)
            inv[i] = (MOD-MOD/i)*inv[MOD%i]%MOD;
    }
    
    int main()
    {
        getInv();
        printf("%lld\n",inv[5]);
        return 0;
    }

    四、递归求逆元

    复杂度:O(logn)

    注意:mod需要是素数(中国剩余定理中不太好用)

    /*
        Time:2018/8/31
        Writer:Sykai
        Function:递归求逆元
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <set>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 100;
    const int MOD = 1e9 + 7;
    typedef long long ll;
    typedef pair<int,int> P;
    ll inv[maxn];//数组的大小需要根据实际情况来调整
    
    ll getInv(ll x)
    {
        if(x == 1) return 1;
        return (MOD-MOD/x)*getInv(MOD%x)%MOD;
    }
    
    int main()
    {
        printf("%lld\n",getInv(5));
        return 0;
    }

    五、求阶乘的逆元

    代码:(不确定怎么用)

    /*
        Time:2018/8/31
        Writer:Sykai
        Function:递归求逆元
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <set>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 100;
    const int MOD = 1e9 + 7;
    typedef long long ll;
    typedef pair<int,int> P;
    ll inv[maxn];//数组的大小需要根据实际情况来调整
    ll fac[maxn+1];//阶乘数组
    ll qpow(ll a,ll b)
    {
        ll res = 1;
        while(b)
        {
            if(b&1)
                res = res * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return res;
    }
    
    
    int main()
    {
        inv[maxn] = qpow(fac[maxn],MOD-2);
        for(ll i = maxn-1; i>=0; i--)
        {
            inv[i] = (inv[i+1]*(i+1))%MOD;
        }
        return 0;
    }

    参考博客:

    https://blog.csdn.net/baidu_35643793/article/details/75268911

    https://blog.csdn.net/xiaoming_p/article/details/79644386

  • 相关阅读:
    Android测试入门篇
    SQL的基本知识
    正则表达式
    ES5语法
    vscode
    继承小结
    工作遇到的问题
    后台程序员的HTTP缓存
    xhr下载图片/服务器向客户端推送消息
    HTTP2.0
  • 原文地址:https://www.cnblogs.com/sykline/p/9737741.html
Copyright © 2011-2022 走看看