费马小定理:
内容:假如a是一个整数,p是一个质数,那么ap - a是p的倍数,可以表示为
ap ≡ a (mod p)
如果a不是p的倍数(即gcd(a, p) == 1),这个定理也可以写成
ap-1 ≡ 1 (mod p)
变形得a * ap-2 ≡ 1 (mod p),所以a关于模p的逆元x = ap-2 (mod p),用快速幂模可快速求之。
算法时间复杂度:O(logn)
模板代码:
LL pow_mod(LL a, LL b, LL p) { //a的b次方取模p
LL ret = 1;
while (b) {
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fermat(LL a, LL p) { //费马小定理求a关于b的逆元
return pow_mod(a, p-2, p);
}
3. 欧拉定理:
欧拉函数:对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(n) = n(1 – 1/p1)(1 – 1/p2)…(1 – 1/pm), pn为n的所有质因数, φ(1) = 1)。
欧拉定理:若gcd(a, p) = 1,则a^φ(p) ≡ 1 (mod p)。
即 a*a^(φ(p)−1) ≡ 1(mod p),那么a关于模p的逆元x = a^(φ(p)−1) (mod p)。
(当p为素数的时候φ(p)=p-1,则φ(p)-1=p-2可以看出欧拉定理是费马小定理的推广)
费马小定理要求模数p为素数,欧拉定理则没有此要求,但是似乎还有个问题?如何判断a是否有逆元呢?
再求一次gcd判断是否互质吗?这还不如直接用扩展欧几里得算法呢。
可以由逆元性质直接检验是否为逆元,看求出的值x与a相乘模p是否为1即可。
算法时间复杂度:O(logn)
模板代码:
#include <iostream>
using namespace std;
typedef long long LL;
LL euler(LL n) { // 欧拉函数
LL res = n, i;
for (i = 2; i * i <= n; 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 pow_mod(LL a, LL b, LL p) { // 快速幂模
LL ret = 1;
while (b) {
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
int main() {
LL a, p, inv;
cin >> a >> p;
inv = pow_mod(a, euler(p) - 1, p); // inv为a关于模p的逆元
if (a * inv % p == 1) cout << inv << endl; // 由逆元性质检验逆元是否存在
else cout << -1 << endl; // 不存在输出-1
return 0;
}