首先附上学习的博客 https://blog.csdn.net/sdfzchy/article/details/76098066
https://blog.csdn.net/STcyclone/article/details/52081822
相关 百科乘法逆元 https://baike.baidu.com/item/%E4%B9%98%E6%B3%95%E9%80%86%E5%85%83/5831857?fr=aladdin
逆元 https://baike.baidu.com/item/%E9%80%86%E5%85%83%E7%B4%A0?fromtitle=%E9%80%86%E5%85%83&fromid=11054145
同余方程 https://baike.baidu.com/item/%E5%90%8C%E4%BD%99%E6%96%B9%E7%A8%8B/9823007
乘法逆元
至于我们为什么要讨论模意义下的运算呢? 例如
1. 很多算法题的数值运算可能会溢出,为了研究和讨论算法,我们引入了模的概念
2. 哈希的时候,数值溢出是很正常的,所以我们需要通过取模,把哈希函数的数值限定在我们可控的范围内,让我们可以继续通过哈希的规则进行存放.
为什么需要乘法逆元
模意义下的加减乘运算都是具有封闭性的,但除法确是例外,
所以我们就要找一种在模意义下代替除法运算的东西.
即是说, 我们在模意义下的除法同一通过乘该数的乘法逆元来等价替代.
什么是乘法逆元呢?
定义: 在a,p互为质数的条件下, 如果 a * b 余 p 等于1
即 a * b % p == 1
则 我们称 b 是 mod p意义下a的逆元.
记作 b = inv(a)
// 注, a * b 余 p 等于1 的通常写法时 a * b 同余 p == 1
例如: a / b % p 因为 模意义下是没有除法的. 但是我们可以转换成乘法做等价替换.
当a 与 p 互质时, a / b % p <==> a * inv(a) % p
同时 这也是我们通常取质数作为模数的原因(同时质数还有费马小定理,可以较快速的求出逆元.)
// 另注:
其他: 关于同余方程
同余方程是一个数学方程式。
该方程式的内容为:对于一组整数Z,
Z里的每一个数都除以同一个数m,
得到的余数可以为0,1,2,...m-1,共m种。
我们就以余数的大小作为标准将Z分为m类。每一类都有相同的余数。
那么 如果求解一个数的逆元呢.
// 记得这个数和模数必须互质哟
方法一: 扩展欧几里得 (求逆元 调用mod_reverse函数)
// 限定条件 a n 互质 log(n)
// @a 数
// @n 模数
inline long long mod_reverse(long long a,long long n)
{
long long x,y,d=extend_gcd(a,n,x,y);
if(d==1) {
if(x%n<=0)return x%n+n; // 答案是负的要置为正的.
else return x%n;
} else return -1ll;
}
// 限定条件 a b 互质
// @a 是 这个数
// @b 是 模数
// @x 是 返回的逆元 返回-1 表示没有逆元
// @y 暂时不知道是什么...
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
if(a==0&&b==0)
return -1ll;
if(b==0)
{
x=1ll;
y=0ll;
return a;
}
long long d=extend_gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
方法二: 费马小定理
// 模数是一个质数 复杂度 log(n)
// 原理, 当p是一个质数时,有 inv(a) = a^(p-2) % p;
long long pwr4(long long a, long long k, long long jmod) {
long long res = 1, base = a;
while (k) {
if (k&1) res = res * base % jmod;
base = base * base % jmod;
k >>= 1;
}
return res;
}
long long mod_reverse(long long a, long long n)
{
return pwr4(a, n-2, n);
}
方法三: 欧拉定理
// 模数不是质数的情况
// 由a^φ(p)≡ 1(mod p) 得 a^(φ(p)−1)是a的逆元
// φ(p)是欧拉函数
// O(n)的时间可以递推出1~n在 mod p 意义下的逆元
// 没用过 所以先给出别人的代码 kuangbin大神板子也有代码.
void inv3(LL mod)//线性递推求逆元
{
inv[1]=1;
for(int i=2;i<=mod-1;i++)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
cout<<inv[i]<<" ";
}
}