乘法逆元
首先附上学习的博客 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]<<" "; } }