逆元的定义
给定正整数a,p,如果有 ,且a与p互质,则称x的最小正整数解为a模p的逆元。
一、扩展欧几里得算法
使用条件:基本上通用,不要求p为质数,且效率高,时间复杂度为。
证明过程:有解的条件是
,即a、p互质,所以根据扩展欧几里得原理,就把问题等价于求解
,就可以使用欧几里得算法了。
void extend_gcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1, y = 0;
return;
}
extend_gcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - (a / b) * y;
}
ll mod_inverse(ll a, ll mod){
ll x, y;
extend_gcd(a, mod, x, y);
return (x % mod + mod) % mod;
}
二、费马小定理
定理内容:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
使用条件:要求p为质数,效率也挺高,但由于扩展欧几里得算法通常情况下更优,因此该方法使用情况较少,时间复杂度为。
证明过程:要证明费马小定理,需要两个引理的辅助:
引理1(剩余系定理2):若a,b,c为任意3个整数,m为正整数,且(m,c) = 1,则当a·c≡b·c(mod m)时,有a≡b(mod m)。
引理2(完全剩余系性质2):若(
)构成模n的完系,
,(m,n) = 1,则
也构成模n的完系。
有了这两个引理,我们就可以开始证明了:
对素数p的完全剩余系,由于(a, p) = 1,根据引理2,
也是p的一个完全剩余系,由完全剩余系的性质,
;
即;
所以得证。
再对其作变形得到;
令a的逆元为,则
。
ll qmi(ll a, ll b, ll mod){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll fermat(ll a, ll mod){
return qmi(a, mod - 2, mod);
}
三、欧拉定理
定理内容:设,且
,则我们有:
,其中
称为对模
缩系的元素个数。
使用条件:不要求p为质数,这相当于费马小定理求逆元的扩展,先求出欧拉函数,再求逆元,时间复杂度为。
证明过程:取模的缩系
,则
也是模
的缩系,故有
得证。
再对其作变形得到;
令a的逆元为,则
。
ll qmi(ll a, ll b, ll mod){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll get_phi(ll n){
ll res = n;
for(int i = 2; i <= n / i; i++){
if(n % i == 0){
res = res / i * (i - 1);
while(n % i == 0)
n /= i;
}
}
if(n > 1)
res = res / n * (n - 1);
return res;
}
ll mod_inverse(ll a, ll mod){
if(a % mod != 0)
return qmi(a, get_phi(mod) - 1, mod);
else
return -1;
}
四、递推打表
递推公式:。
使用条件:要求p为质数,也是一种常用的求逆元的方法,时间复杂度为。
证明过程:
证毕。
ll inv[N]; // 逆元表
void mod_inverse(ll n, ll mod){
inv[0] = inv[1] = 1;
for(int i = 2; i <= n; i++)
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}