深入浅出乘法逆元

Posted kcfzyhq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出乘法逆元相关的知识,希望对你有一定的参考价值。

深入浅出乘法逆元
 1.模的运算律
 2.定义
 3.求解
  3.1费马小定理
  3.2扩展欧几里得算法
  3.3线性求解

深入浅出乘法逆元

模的运算律

先来一波模运算律表:
运算律 内容
交换律 \\((a+b)\\%p=(b+a)\\%p\\)
\\((a\\times b)\\%p=(b\\times a)\\%p\\)
结合律 \\(((a+b)\\%p+c)\\%p=(a+(b+c)\\%p)\\%p\\)
\\(((a\\times b)\\%p\\times c)\\%p=(a\\times (b\\times c)\\%p)\\%p\\)
分配率 \\(((a+b)\\%p\\times c)\\%p=((a\\times c)\\%p+(b\\times c)\\%p)\\%p\\)
\\((a\\times b)\\%p=(a\\%p\\times b\\%p)\\%p\\)
\\((a+b)\\%p=(a\\%p+b\\%p)\\%p\\)
\\((a-b)\\%p=(a\\%p-b\\%p)\\%p\\)

定义

有的时候我们需要对一个数取模,这很简单。但是在取模的过程中出现了除数,那么取模就没这么简单了: $$\\frac{7}{2}\\%4=3\\%4=3$$注意:$\\frac{7\\%4}{2}=\\frac{3}{2}=1$是错误的 但万一是$\\frac{7^{10000}}{2}\\%4$计算机可无法先计算$\\frac{7^{10000}}{2}$再$\\pmod4$,因为数字太大了。 这个时候我们就需要用到乘法逆元了,事实上:$\\frac{7^{10000}}{2}=(7*3)^{10000}$。我们运用模的运算律可以通过边乘边取模即可得到答案,其中3是7在$\\pmod 4$意义下的逆元。 关于逆元的严格定义如下: >$若整数b,m互质,并且b\\mid a,则存在整数x,使得a/b\\equiv a*x\\pmod m,则称x为b的模m乘法逆元,记为b^{-1}\\pmod m$

求解

3.1费马小定理
[^1] 因为$a/b\\equiv a*b^{-1}\\equiv a/b*b*b^{-1}\\pmod m$,所以$b*b^{-1}\\equiv 1\\pmod m$[^2] 如果m是质数(此时我们用符号$p$代替$m$)并且$bint ksm(int a,int b,int p) { int ans=1; for(; b; b>>=1,a=a*a%p)if(b&1)ans=ans*a%p; return ans; }

时间复杂度是\\(O(\\log n)\\)

3.2扩展欧几里得算法
扩展欧几里得算法的具体内容参考我写的:[浅析扩展欧几里得算法(exgcd)](http://www.cnblogs.com/kcfzyhq/p/8485063.html) 根据逆元的定义我们要求的是$a*x\\equiv1\\pmod m$关于x的同余方程,其中x为a在$\\pmod m$意义下的逆元 事实上$$ax=my+1······①$$也就是$ax\\div m=y······1$ 变形①式得:$$ax-my=1$$ 既然$a,b$都已知,就不难求出$x和y$了(但要注意$y$的系数$-b$必须是正整数,因为在计算机计算过程中如果模数是负的将导致结果出错)如果$-b$不是正整数,我们同时改变$a,b$的符号即可。扩展欧几里得算法代码如下: ```cpp int exgcd(int a,int b,int &x,int &y) { if(b) { int c=exgcd(a,b,y,x); y-=a/b*x; return c; } else { x=1; y=0; return a; } } ``` 时间复杂度是$O(\\ln n)$
3.3线性求解
当我们需要求解大量的逆元的时候,前两种的方法时间复杂度都要乘以$n$,时间复杂度都不是很理想。所以我们就用$O(n)$的时间来快速求解。具体做法如下:假设我们要求x的逆元,那么:$$m=k*x+r$$$$k*x+r\\equiv0\\pmod m$$同乘以$x^{-1}*r^{-1}$得:$$k*r^{-1}+x^{-1}\\equiv0\\pmod m······①$$将②式变形得:$$x^{-1}\\equiv-k*r^{-1}\\pmod m$$ 所以我们得到:$$x^{-1}=-\\lfloor m/x\\rfloor*(m\\%x)^{-1}$$ 那么只要建一个数组inv,初始值inv[1]=1。所以代码如下:
	for(int i=2; i<=n; i++)
		inv[i]=-(p/i)*inv[p%i];

以上是关于深入浅出乘法逆元的主要内容,如果未能解决你的问题,请参考以下文章

[模板]乘法逆元

转载 乘法逆元

乘法逆元(P3811)

乘法逆元简单说说乘法逆元的求法

乘法逆元怎么计算

乘法逆元(转)