乘法逆元

定义

对于\(ax ≡ 1(mod p)\), 我们将使恒等式成立的最小正整数\(x\)称为\(a\)的逆元。
通过公式我们可以推出\(ax+py = 1(x,y为整数)\)所以根据裴蜀定理,\(gcd (a, p) ≡ 0 (mod 1)\), 即是\(gcd (a, p) = 1\)也就是\(a\)\(p\)互质。

用处

\((a/b)\%q=(a*(1/b))\%q=(a*b^{-1})\%q\)
从这个等式中, 我们可以看出, 我们求\((a/b)\%q\)其实可以求\(a\)乘以\(b\)的逆元去模\(p\), 所以逆元的作用就是可以在模p的意义下代替除法。

求法

费马小定理

这个方法是最常用的, 根据费马小定理:

\(a, p\)互质,且\(p\)为质数,则\(a^{p - 1}≡1(mod p)\)

根据这个公式, 两边同除\(a\)
\(a^{p - 2}≡a^{-1}(mod p)\)
所以我们可以用\((a^{p-2}\%p + p)\%p\)求出逆元\((其中加上p是为了防止为负的情况)\)
代码:

long long fast_mul (long long a, long long b, long long p) {//快速乘, 防止long long的积溢出
	long long ans = 0;
	
	a %= p;
	while (b) {
		if (b & 1) {
			ans += a;
			ans %= p;
		}
		a = a << 1;
		a %= p;
		b >>= 1;
	}
	
	return ans;
}
long long fast_pow (long long a, long long b, long long p) {
	long long ans = 1;
	
	a %= p;
	while (b) {
		if (b & 1) {
			ans = fast_mul (ans, a, p);
			ans %= p;
		}
		a = fast_mul (a, a, p);
		a %= p;
		b >>= 1;
	}
	
	return ans;
}
long long inv (long long a, long long p) {//求逆元
	return (fast_pow (a, p - 2, p) + p) % p;
}

拓展欧几里得

逆元的定义:
\(ax ≡ 1(mod p)\)
\(=>ax-py=1\)
然后我们就可以用拓展欧几里得求出\(x\)的特解, 然后再通过这个特解求出\(x\)的最小正整数解。
代码:

void ex_gcd (long long a, long long b, long long &x, long long &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		
		return ;
	}
	ex_gcd (b, a % b, x, y);
	
	long long r = x;
	
	x = y;
	y = r - a / b * y;
}
long long inv (long long a, long long p) {
	long long x, y;
	
	ex_gcd (a, p, x, y);
	
	return (x + p) % p;
}

递推与递归

我们设\(p%i\)为r, \(\lfloor\frac{p}{i}\rfloor\)\(k\):
\(p = i * k + r\)
又因为\(p ≡ 0(mod p)\)
所以\(i * k + r ≡ 0(mod p)\)
两边同时乘以\(i^{-1}*r^{-1}\)
\(i * k * i^{-1}*r^{-1} + r * i^{-1} * r^{-1} ≡ 0(mod p)\)
因为\(i*i^{-1} ≡ 1(mod p),r*r^{-1} ≡ 1(mod p)\)
所以\(k * r^{-1} + i^{-1} ≡ 0(mod p)\)
\(k * r^{-1}\)移到右边得
\(i^{-1} ≡ -k * r^{-1}(mod p)\)
\(=>i^{-1} ≡ p - k * r^{-1}(mod p)\)
最终得到\(i^{-1} = (p - k * r^{-1} \% p) \% p\)
代码:

long long inv (long long a, long long p) {
	if (a == 0 || a == 1) {
		return 1;
	}
	
	return (p - (p / a) * inv (p % a, p) % p) % p; 
}

不过如果这个式子最大的用处还是两处:
1.递归时可以用记忆化进行优化
2.可以在线性时间内用递推求出\([1, n]\)中所有数在模p意义下的逆元
关于第二个, 我们来分析一下。
首先肯定\(p \% a < a\)
那么每一个的逆元又只跟\(p % a\)的逆元有关
所以这个关系式是线性的
代码:

void inv (long long n, long long p) {
	s[1] = 1;
	s[0] = 1;
	for (int i = 2; i <= n; i ++) {
		s[i] = (p - ((p / i) * s[p % i]) % p) % p;
	}
}
posted @ 2020-10-24 16:46  小篪篪  阅读(92)  评论(0编辑  收藏  举报
Live2D