逆元的三种求法 (费马小定理,扩展欧几里得,递推求阶乘逆元)

逆元的三种求法

费马小定理,扩展欧几里得,递推求阶乘逆元

逆元

对于一个实数 A A 如果存在一个 x x 使得 A x = 1 Ax = 1 ,我们就把这个 x x 叫做 A A 的逆元,记做 x = A 1 x = A^{-1}
在一般数学中,我们所说的逆元就是倒数

但是在数论中,如果一个数字 A A 存在一个对 p p 的逆元 x x ,就可以写成 A x 1   m o d   p Ax≡1\ mod\ p 的形式(此处 p p A A 互质,若不互质,则不存在逆元)。

逆元的作用

我们知道 取余 的性质:

  1. ( a b ) % c = ( a % c b % c ) % c (a - b)\%c = (a\%c - b\%c)\%c
  2. ( a + b ) % c = ( a % c + b % c ) % c (a + b)\%c = (a\%c+b\%c)\%c
  3. ( a × b ) % c = ( a % c × b % c ) % c (a\times b)\%c=(a\%c\times b\%c)\%c

对于基本的四种运算而言,加减乘都符合“分配率”,唯独除法不满足。

( a ÷ b ) % c = ( a % c ÷ b % c ) % c (a\div b)\%c=(a\%c\div b\%c)\%c

上面这种运算是错误的!

如果要实现这种运算,就要把除法转化为乘法,假设 b 1 b^{-1} b b 关于 c c 的逆元。
( a ÷ b ) % c (a\div b)\%c 可以转化为 ( a × b 1 ) % c = ( a % c × b 1 % c ) % c (a\times b^{-1})\%c=(a\%c\times b^{-1}\%c)\%c

逆元求法

费马小定理

费马小定理:假设 p p 是一个质数,且 g c d ( a , p ) = 1 gcd(a, p) = 1 ,那么 a p 1 1   m o d   p a^{p-1}≡1\ mod\ p
我们也可以的得到一个费马小定理的特例:假设 a a 是一个整数,且 g c d ( a , p ) = 1 gcd(a, p) = 1 ,那么 a p 1 1   m o d   p a^{p-1}≡1\ mod\ p

根据费马小定理 a p 1 1   m o d   p a^{p-1}≡1\ mod\ p ,可以发现 a p 2 × a 1   m o d   p a^{p-2}\times a≡1\ mod\ p 也成立。
是不是很像上面说到的逆元的形式: A x 1   m o d   p Ax≡1\ mod\ p x x A A 关于 p p 的逆元。
那根据费马小定理也可得知 a p 2 a^{p-2} a a 关于 p p 的逆元。
所以求 a a 的逆元,就直接用快速幂求 a p 2 a^{p-2} 就可以了。

LL power(LL a, int x) {
	LL ans = 1;
	while(x) {
		if(x&1) ans = (ans * a) %mod;
		a = (a * a) %mod;
		x >>= 1;
	}
	return ans;
} 

LL inv(LL a) {
	return power(a, mod - 2);
}

扩展欧几里得

扩展欧几里得: a x + b y = g c d ( a , b ) ax +by=gcd(a,b) 的解一定存在。
当我们要求 a a 关于 p p 的逆元时,若逆元存在,则 g c d ( a , p ) = 1 gcd(a,p)=1 。假设逆元为 x x ,即: a x 1   m o d   p ax ≡ 1\ mod\ p
我们可以展开一下变成 a x = 1 + p k ax = 1 + pk ,由于 k k 可正可负。
所以我们可以得到 a x + p k = 1 ax + pk=1 ,其实就是 a x + p k = g c d ( a , p ) ax + pk= gcd(a,p)
所以我们用扩展欧几里得求出一个最小的 x x 就是 a a 关于 p p 的一个逆元啦。


我们来试着解这个欧几里得吧!
现在已经有了 a x + b y = g c d ( a , b ) ax + by=gcd(a,b) 了。我们想试着求出一个特解 x x

根据欧几里得算法我们可以知道 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b)

而且我们可以看出 b x + ( a % b ) y = g c d ( b , a % b ) bx+(a\%b)y=gcd(b,a\%b)
由此我们可得:(由于两边的 x x y y 值不同,我们用 x x' y y' 进行区分)
b x + ( a % b ) y   =   a x + b y bx'+(a\%b)y'\ =\ ax + by
我们想要把式子化简一下,可以从 a % b a\%b 入手,即 a % b   =   a a b × b a\%b\ =\ a-\lfloor\frac{a}{b}\rfloor \times b
所以我们可以化简得到: a x + b y   =   b x + ( a a b b ) y ax + by\ =\ bx'+(a-\lfloor\frac{a}{b}\rfloor b)y'
移项:
a x + b y = a y + b ( x a b y ) ax + by=ay'+b(x'-\lfloor\frac{a}{b}\rfloor y')
系数相等,所以我们可以解得
{ x = y y = ( x a b y ) \begin{cases} x=y'\\ y=(x'-\lfloor\frac{a}{b}\rfloor y')\\ \end{cases}
根据欧几里得算法,我们一直递归下去,总会到要一个最终位置的 a % b = 0 a\%b=0
所以式子变成了 a x = g c d ( a , b ) ax=gcd(a,b) 。此时我们取一个特解 x = 1 x=1 y = 0 y=0 。然后往回推,就可以得到一开始的那个 x x
此时解出来的 x x 就是 a a 关于 p p 的一个逆元。

void exgcd(LL a, LL b, LL &x, LL &y) {
	if (b == 0) {
		x = 1;y = 0;
	} else {
		exgcd(b, a%b, y, x);
		y -= (a/b) * x;
	}
}

递推求阶乘逆元。

我们经常会用到阶乘的逆元,我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推,直接看代码再解释。

void init() {
	fact[0] = 1;
	for (int i = 1; i < maxn; i++) {
		fact[i] = fact[i - 1] * i %mod;
	}
	inv[maxn - 1] = power(fact[maxn - 1], mod - 2);
	for (int i = maxn - 2; i >= 0; i--) {
		inv[i] = inv[i + 1] * (i + 1) %mod;
	}
}

我们可以假设把 n ! n! 的逆元表示为 [ n ! ] 1 [n!]^{-1}
我们要求 ( n 1 ) ! (n-1)! 的逆元,我们可以考虑给 ( n 1 ) ! (n-1)! 乘上一个 n n 把他变为 n ! n!
( n 1 ) ! × n [ n ! ] 1 1   m o d   p (n-1)!\times n[n!]^{-1}≡1\ mod\ p
因此 n [ n ! ] 1 n[n!]^{-1} ( n 1 ) ! (n-1)! 关于 p p 的一个逆元。

猜你喜欢

转载自blog.csdn.net/qq_40861916/article/details/82928080