乘法逆元
费马小定理 + 快速幂
求b mod p 的乘法逆元x(bx = 1(mod p))
- 应用前提:a 与 p互质,且p为质数
原因:该算法的理论前提为费马小定理,所以与费马小定理的要求相同
- 逆元的用途
根据上面逆元的表达式,
bx = 1 (mod p)
a*bx = a (mod p)
ax = a / b (mod p)
也就是说当我们需要计算 a / b (mod p) 的结果只需要计算ax (mod p)的结果即可,从而解决了除法取模的问题
!!!!!!残留的问题是我们的推导过程中a是不是需要和p互质,需要看一下书
#include <iostream>
using namespace std;
typedef long long LL;
LL qpow(int a, int b, int p)
{
LL res = 1;
while (b)
{
if (b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int a, p;
cin >> a >> p;
int res = qpow(a, p - 2, p);
/**
* 此题中规定了p为质数,而费马小定理的条件是a和p互质且p为质数,那么就要求a不能是p的倍数
* 然后考虑到如果a是p的倍数,那么a%p==0, 那么a^(p - 2)%p也等于0,所以只需要通过res的值是否为0即可判断是否存在逆元
*
* 但是有一种特殊情况,当p为2时候,我们计算的就是任何数的0次幂,结果均是1,即使当a为p的倍数这种非法情况时我们也无法通过res判断出来
*
* 所以不能通过res来判断答案是否存在
*/
if (a % p) cout << res << endl;
else cout << "impossible" << endl;
}
return 0;
}
扩展欧几里得算法
适用场景
求单个数据的逆元且不能满足费马小定理的条件
原理
$ aa^{-1} = 1 (mod m) $, 求解a模m的乘法逆元即求解 ax = 1 (mod m) 的一组解,即求解 ax + my = 1 的一组解
代码实现
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
int gcd = exgcd(b, a % b, y, x);
y -= a / b * x;
return gcd;
}
int main()
{
int a, m, x, y;
cin >> a >> m;
int t = exgcd(a, m, x, y);
// 有解的条件为1是gcd(a, m)的倍数
if (t != 1) cout << "impossible" << endl;
else cout << x << endl;
return 0;
}
线性递推
适用场景
求1到n所有数据的逆元,此时如果对每一个数采用快速幂或扩展欧几里得计算太慢了
计算原理:
模m条件下,1的逆元inv[1] = 1,这是下面推算其它情况的前提
设 $ t = \frac{M}{i}, k = M % i $
可推导出
-> \(t * i + k = 0 (mod M)\)
-> \(-t * i = k (mod M)\)
上式左右两边同时除 \(i * k\),或者说左右两边同时乘以 \(i^{-1} * k^{-1}\), 可得到
\(-t * inv[k] = inv[i] (mod M)\)
将t 和 k 进行替换可得 \(inv[i] = (M - \frac{M}{i}) * inv[M \% i] \% M\) ,其中使用 \(M - \frac{M}{i}\) 防止出现负数
计算过程中,如果出现M % i == 0,使用到inv[0]的情况,表示在模M条件下,求i的逆元,其中i与M并不互质,所以 \(ix = 1 (mod M)\) 是无解的,即此时i不存在逆元。
代码实现
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n, m;
int inv[N];
int main()
{
cin >> n >> m;
inv[1] = 1;
/**
* 当i == m时,取到inv[0],计算的inv[m] = 0
* 此后,i>m,所以m%i=m,所以计算得到的inv[i]都等于inv[m]=0
* 所以难道是我们利用的时候需要考虑这个问题?还是代码有问题?待定
*/
for (int i = 2; i <= n; ++ i)
inv[i] = (long long)(m - m / i) * inv[m % i] % m;
for (int i = 2; i <= n; ++ i)
cout << i << ' ' << inv[i] << endl;
return 0;
}