若在mod p意义下,对于一个整数a,有a*b≡1(mod p),那么这个整数b即为a的 乘法逆元,同时a也为b的乘法逆元
一个数有逆元的充分必要条件是gcd(a,p)=1,此时a才有对p的乘法逆元
求取 (a/b)%p 等同于 求取 a∗(b的逆元)%p
- 线性递推
当p是个质数的时候有
inv(a) = (p - p / a) * inv(p % a) % p
证明:
设x = p % a,y = p / a
于是有 x + y * a = p
(x + y * a) % p = 0
移项得 x % p = (-y) * a % p
x * inv(a) % p = (-y) % p
inv(a) = (p - y) * inv(x) % p
于是 inv(a) = (p - p / a) * inv(p % a) % p
然后一直递归到1为止,因为1的逆元就是1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll inv[3000005];
int main(){
ll n,p;
cin >> n >> p;
inv[1]=1;
printf("%lld\n",inv[1]);
for(ll i=2;i<=n;++i){
inv[i]=(p-p/i)*inv[p%i]%p;
printf("%lld\n",inv[i]);
}
return 0;
}
- 适用于mod是不太大的素数,连续求多个逆元
- 时间复杂度O(n)
- 费马小定理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ksm(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a*a%p;
b/=2;
}
return ans;
}
int main(){
ll n,p;
scanf("%lld%lld",&n,&p);
cout << ksm(n,p-2,p) << endl;
return 0;
}
- mod是素数
- 时间复杂度O(logmod)
- 扩展欧几里得
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1,y=0;
}
exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
int main(){
ll n,p;
scanf("%lld%lld",&n,&p);
ll x,y;
exgcd(n,p,x,y);
cout << (x%p+p)%p << endl;
return 0;
}
- 只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法
- 时间复杂度O(logn)(实际是斐波那契数列)
- 欧拉函数
a,p互素,则有aφ(p)=1(modp)
则aφ(p)−1∗a=1(modp)
即aφ(p)−1为a mod p意义下的逆元
#include<bits/stdc++.h>
typedef long long ll;
ll get_euler(ll x) {
ll ans=x;
for(ll i=2;i*i<=x;i++)
if(x%i==0) {
ans=ans/i*(i-1);
while(ans%i==0) ans/=i;
}
if(ans>1) ans=ans/x*(x-1);
return ans;
}
ll quickpowmod(ll a,ll b,ll mod) {
ll ans=1;
while(b) {
if(b&1) ans=(ans*a)%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int main()
{
ll a,p,x,y;
scanf("%lld%lld",&a,&p); ///a与p互素
ll inva=quickpowmod(a,get_euler(p)-1,p);
printf("%lld",inva);
return 0;
}
- 基本上不常用
- 时间复杂度O(logmod)