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

逆元的三种求法

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

逆元

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

但是在数论中,如果一个数字 AAA 存在一个对 ppp 的逆元 xxx,就可以写成 Ax≡1 mod pAx≡1\ mod\ pAx1 mod p 的形式(此处 pppAAA 互质,若不互质,则不存在逆元)。

逆元的作用

我们知道 取余 的性质:

  1. (a−b)%c=(a%c−b%c)%c(a - b)\%c = (a\%c - b\%c)\%c(ab)%c=(a%cb%c)%c
  2. (a+b)%c=(a%c+b%c)%c(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÷b)%c=(a%c÷b%c)%c(a\div b)\%c=(a\%c\div b\%c)\%c(a÷b)%c=(a%c÷b%c)%c

上面这种运算是错误的!

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

逆元求法

费马小定理

费马小定理:假设 ppp 是一个质数,且 gcd(a,p)=1gcd(a, p) = 1gcd(a,p)=1,那么 ap−1≡1 mod pa^{p-1}≡1\ mod\ pap11 mod p
我们也可以的得到一个费马小定理的特例:假设 aaa 是一个整数,且 gcd(a,p)=1gcd(a, p) = 1gcd(a,p)=1,那么 ap−1≡1 mod pa^{p-1}≡1\ mod\ pap11 mod p

根据费马小定理 ap−1≡1 mod pa^{p-1}≡1\ mod\ pap11 mod p ,可以发现 ap−2×a≡1 mod pa^{p-2}\times a≡1\ mod\ pap2×a1 mod p 也成立。
是不是很像上面说到的逆元的形式:Ax≡1 mod pAx≡1\ mod\ pAx1 mod pxxxAAA 关于 ppp 的逆元。
那根据费马小定理也可得知 ap−2a^{p-2}ap2aaa 关于 ppp 的逆元。
所以求 aaa 的逆元,就直接用快速幂求 ap−2a^{p-2}ap2 就可以了。

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);
}
扩展欧几里得

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


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

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

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

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! 的逆元表示为 [n!]−1[n!]^{-1}[n!]1
我们要求 (n−1)!(n-1)!(n1)! 的逆元,我们可以考虑给 (n−1)!(n-1)!(n1)! 乘上一个 nnn 把他变为 n!n!n!
(n−1)!×n[n!]−1≡1 mod p(n-1)!\times n[n!]^{-1}≡1\ mod\ p(n1)!×n[n!]11 mod p
因此 n[n!]−1n[n!]^{-1}n[n!]1(n−1)!(n-1)!(n1)! 关于 ppp 的一个逆元。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值