预防针:本文排版很乱,介绍概念时并不专业(不过能用就好了)
这里有专业的讲解,但是我不怎么看的懂就是了:https://blog.csdn.net/xiaoming_p/article/details/79644386
Part I 概念介绍:
我们想要(A/B)%P,其中A,B代表一个式子,因此可能本身A,B就超过自己的数据范围,都爆了,还怎么除呢?
我们能想到的是(A%P)/(B%P)%P (A%P具体举栗子就是A = a*b,然后(a%P)*(b%P)%P)
不过这样算出来的答案是错误的,很明显啦,因为是除法呀。(总的来说我讲不清)
所以我们用乘法做,乘法各自取模再相乘是不会出错的。
(A%P)*((1/B)%P)%P
但是很明显1/B是个小数啊。。。
所以我们要找一个数X,使得A*X%P = (A/B)%P
X就是B 在 模 P 下的逆元
Part II:逆元的求法
① 利用一个结论:(适用于P是质数)
当P为质数时:X^P %P = X%P
我们左右各除X^2,得到:X^(P-2) %P =(1/X)%P
因此,结论为:求B在P下的逆元,只要P是质数,B的逆元 = B^(P-2)
因为P可能很大,所以用快速幂。
代码:
#include
#include
#define ll long long
#define mod (ll)1000000007
using namespace std;
ll fast_power(ll a,ll b)
{
ll ans = 1;
while (b){
if (b&1) ans = (ans*a)%mod;
b >>=1;
a = (a*a)%mod;
}
return ans;
}
int main()
{
ll a;
printf("输入一个数求它在模1e9+7下的逆元\n");
scanf("%lld",&a);
ll ans = fast_power(a,mod-2);
printf("%lld%在 1e9+7 下的逆元=%lld\n",a,ans);
return 0;
}
②利用exgcd(扩展欧几里得算法)求解二元一次方程(P是不是质数都可以)
关于exgcd可以参考这篇:https://blog.csdn.net/weixin_43768644/article/details/94771375
又有一个结论:
A在模P下的逆元X,满足 A*X%P = 1%P (已知X%P = (1/A)%P对吧,两边乘A就可以了)
这个式子可以理解为 A*X = 1+k*P,就是A*逆元 = 整数倍的P+1
A,P已知,那么就变成了二元一次方程:A*X-k*P = 1 的求解问题,用exgcd即可
然后:一定要扔P,不要扔-P到exgcd。
扔-P你求的是
A*x - P*y = (gcd(A,-P)=-1)的解
扔P求的是
A*x + P*(-y)=(gcd(A,P)=1) ///虽然y反了,但是x没有问题
根据exgcd存在解的先决条件来看(上面那篇里面有讲到),(A/B)%P,B存在逆元的条件是A,B互质,因为exgcd只能求
ax+by=gcd(a,b)的解,如果a,b互质,那么gcd(a,b)>1,原式无解
代码:
#include
#include
#define ll long long
#define mod (ll)1000000007
using namespace std;
void exgcd(ll a,ll b,ll& x,ll& y)
{
if (b==0){x = 1;y = 0;}
else {
exgcd(b,a%b,y,x);
y = y - (a/b)*x;
}
}
int main()
{
ll a;
printf("输入一个数,求它在1e9+7下的逆元\n");
scanf("%lld",&a);
ll x,y;
exgcd(a,mod,x,y);///bx + p(-y) = 1
x = (x+mod)%mod;
printf("%lld%在 1e9+7 下的逆元=%lld\n",a,x);
return 0;
}