1.使用情况:
用法1.在求解除法取模问题(a/b)%m时,我们可以转化为(a%(b∗m))/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的逆元,即b∗c≡1(modm),
那么有a/b=(a/b)∗1=(a/b)∗b∗c=a∗c(modm)
即,除以一个数取模等于乘以这个数的逆元取模。
2.方法:
- 逆元求解一般利用扩欧。
- 当m为质数的时候直接使用费马小定理,m非质数使用欧拉函数。
- 当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);
}
}
来源:https://www.cnblogs.com/aiguona/p/7642683.html