乘法逆元

1.使用情况:

  用法1.在求解除法取模问题(a/b)%m时,我们可以转化为(a%(bm))/b, 
  但是如果b很大,则会出现爆精度问题,所以我们避免使用除法直接计算。 

  用法2.当我们要求(a/b) mod m的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元。
  我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,即(a*k) mod m。其结果与(a/b) mod m等价。

 

  遇到上面的情况,我们可以使用逆元将除法转换为乘法: 

  假设b存在乘法逆元,即与m互质(充要条件)。设c是b的逆元,即bc1(modm)

  那么有a/b=(a/b)1=(a/b)bc=ac(modm) 

  即,除以一个数取模等于乘以这个数的逆元取模。

 

2.方法:

  1. 逆元求解一般利用扩欧。
  2. m为质数的时候直接使用费马小定理,m非质数使用欧拉函数。
  3. m为质数的时候,神奇的线性方法。

 

3.代码:

#include<cstdio>
#include <math.h>
using namespace std;
typedef long long ll;
const int p = 1e9 + 7;
const int N = 1e5 + 5;
ll inv[N];

ll inv1(ll a)//递推法求逆元
{
    return a==1?1:(p-p/a)*inv1(p%a)%p;
}

void inv2(ll n)//通过递推进行逆元打表
{
    inv[1] = 1;
    for (ll i=2; i<=n; ++i)
    {
        inv[i] = (ll) (p - p / i) * inv[p%i] % p;
    }
}

void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x*(a/b);
    }
}
ll inv3(ll a)//扩展欧几里得
{
    ll d, x, y;
    exgcd(a, p, d, x, y);
    return d == 1 ? (x+p)%p : -1;
}

ll inv4(ll a, ll b)//费马小定理
{
    ll res = 1;
    while(b)
    {
        if(b&1)
            res = (res*a)%p;
        b = b>>1;
        a = (a*a)%p;
    }
    return res;
}

int main()
{
    ll b;
    while(1)
    {
        scanf("%lld",&b);
        ll ans1=inv1(b);
        printf("%lld\n",ans1);
        inv2(100);//100以内的数对P的逆元
        ans1=inv[b];
        printf("%lld\n",ans1);
        ans1=inv3(b);
        printf("%lld\n",ans1);
        ans1=inv4(b,p-2);
        printf("%lld\n",ans1);
    }
}

 

 

 

 

 

  

posted @ 2017-10-09 19:45  爱国呐  阅读(217)  评论(0编辑  收藏  举报