逆元(关于除法取模)
**费马小定理:**假如 p 是质数,且 gcd(a, p) = 1,那么 a(p-1) ≡ 1(mod p)。即:假如 a 是整数,p 是质数,且 a, p 互质(即两者只有一个公约数 1),那么 a 的 (p-1) 次方除以 p 的余数恒等于 1。
**逆元:**对于正整数 a 和 p,若 a * x % p ≡ 1(即 ax ≡ 1(mod p)),则称 x 为 a % p 的逆元,即把这个同余方程中 x 的最小正整数解叫做 a 模 p 的逆元。
一个数有逆元的充分必要条件是 gcd(a, p) = 1,此时逆元唯一存在。
除法取模
a/b mod m 等价于 a*b’ mod m (b’ 是 b 模 m 的逆元)
下面是几种求逆元的方法
1、扩展欧几里得
#include<stdio.h>
#define ll long long
ll exgcd(ll b,ll m,ll& x,ll& y){
if(!m){
x=1;
y=0;
return x;
}
else{
exgcd(m,b%m,y,x);
y-=x*(b/m);
}
return x;
}
int main(){
ll b,m,x,y;
while(scanf("%lld%lld",&b,&m)!=EOF){ //b有逆元的前提gcd(b,m)=1
x=exgcd(b,m,x,y);
x=(x%m+m)%m;
printf("%lld\n",x);//x即为b的逆元b'
}
return 0;
}
2、费马小定理
在模 m 为素数的条件下有 b(m-1) = 1 (mod m);
则 b 模 m 的逆元 b’ = b(m-2)
用快速幂求即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
#define LL long long
using namespace std;
LL b,m;
LL pow(LL x,LL n){
LL res=1;
while(n>0){
if(n&1)
res=(res*x);
x=(x*x);
n>>=1;
}
return res;
}
int main(){
while(cin>>b>>m){
cout<<pow(b,m-2)%m<<endl;
}
return 0;
}
3、求逆元的一般公式
a/b mod m = a mod (m*b) /b
注意:实际上a mod (m*b)/b这种的对于所有的都适用,不区分互不互素,而费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素,如果 a 与 m 不互素,那就没有逆元,这个时候需要 a mod (bm)/b 来搞(此时就不是逆元的概念了)。但是当 a 与 m 互素的时候,bm 可能会很大,不适合套这个一般公式,所以大部分时候还是用逆元来搞.