逆元--除法取模

逆元

  • 定义
  • 用途
  • 算法
    • 扩展欧几里得求逆元
    • 费马小定理或欧拉定理
    • 公式递推


定义

定义:如果 a ∗ x = 1 ( m o d p ) , 且 g c d ( a , p ) = 1 a*x = 1(mod p),且gcd(a, p) = 1 ax=1(modp)gcd(a,p)=1(等价于a,p互质),那 x 就为模 p 意义下 a 的乘法逆元。


用途

小定理: ( a / b ) % p = ( a ∗ x ) % p = 1 (a/b) \% p = (a*x) \%p= 1 % p (a/b)%p=(ax)%p=1;(x为模p意义下a的逆元)
证明: a / b ∗ b ∗ x ≡ a ∗ x ( m o d   p ) 且 b ∗ x = 1 ( m o d p ) a/b * b * x\equiv a * x (mod~p) 且b * x = 1(mod p) a/bbxax(mod p)bx=1(modp)
将除法转换成了乘法,就可以直接取模了。


算法

扩展欧几里得求逆元

a ∗ x ≡ 1 ( m o d   p ) a*x \equiv1(mod ~p) ax1(mod p)
扩展欧几里德算法
在这篇博客举的例子就是关于上面这个公式的求解。
贴份代码。

#include
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll &x, ll &y) {
	if(!b) {
		x = 1, y = 0;
		return ;
	}
	exgcd(b, a%b, x, y);
	ll temp = x;
	x = y;
	y = temp - a/b*y;
}
int main() { 
	ll n, m, x, y;
	scanf("%lld%lld", &n, &m);
	exgcd(n, m, x, y);
	printf("%lld", (x%m + m)%m);
	return -0;
} 

费马小定理或欧拉定理

逆元--除法取模_第1张图片
费马小定理: p 是 素 数 , a 是 整 数 , g c d ( a , p ) ≡ 1 ( m o d   p ) = > a p − 1 ≡ 1 ( m o d   p ) p是素数,a是整数,gcd(a,p) \equiv1(mod ~p)=>a^{p-1}\equiv1(mod ~p) pagcda,p)1(mod p)=>ap11(mod p)
逆元定义: a ∗ x = 1 ( m o d p ) , 且 g c d ( a , p ) = 1 = > x 是 a 在 模 p 下 的 逆 元 a *x = 1(mod p),且gcd(a, p) = 1=>x是a在模p下的逆元 ax=1(modp)gcd(a,p)=1=>xap
可以看出 a p − 2 a^{p-2} ap2就是a在模p下的逆元
求个快速幂就行了。

ll Power(ll x, ll y) {
    ll n = y, ans = 1, res = x;
    while(n){
        if(n & 1) ans = ans * res % mod;
        n >>= 1;
        res = res * res % mod;
    }
    return ans%mod;
}

例题:Walk
用阶乘的乘除法求组合数,除法取模要用到逆元。
费马小定理只适用于p为素数的时候,当p不是素数时就可以用上欧拉定理了。

欧拉定理(Euler’s theorem): g c d ( a , p ) = 1 = > a φ ( p ) ≡ 1 ( m o d   p ) gcd(a, p) = 1=>a^{\varphi(p)}\equiv1(mod~p) gcd(a,p)=1=>aφ(p)1(mod p)
其中 φ ( p ) \varphi(p) φ(p)表示的是小于p且和p互质的数的个数。假如p是质数,那么 φ ( p ) \varphi(p) φ(p)就等于 p − 1 p-1 p1,也就是上面的费马定理。
欧拉函数 φ ( p ) \varphi(p) φ(p)是有通式的(还不会),先贴份链接 欧拉函数的定义与计算

公式递推

用来求
已知 1 的 逆 元 1 − 1 = 1 1的逆元1^{-1}=1 111=1,我们将逆元存在inv数组中,inv[1] = 1。
k = p / i , r = p % i k=p/i,r=p\%i k=p/ir=p%i
k ∗ i + r = p = > k ∗ i + r ≡ 0 ( m o d   p ) k*i+r=p=>k*i+r\equiv0(mod~p) ki+r=p=>ki+r0(mod p)
在左右同乘 i − 1 , r − 1 = > k ∗ r − 1 + i − 1 = 0 i^{-1},r^{-1}=>k*r^{-1}+i^{-1}=0 i1r1=>kr1+i1=0
所以 i − 1 = − k ∗ r − 1 < = > i − 1 = − ( p / i ) ∗ ( p % i ) − 1 i^{-1} = -k*r^{-1}<=>i^{-1}=-(p/i)*(p\%i)^{-1} i1=kr1<=>i1=(p/i)(p%i)1
( p / i ) (p/i) (p/i)是一个实数, ( p % i ) − 1 (p\%i)^{-1} (p%i)1是小于在第i个逆元之前的逆元,按递推可以求得。

#include
using namespace std;
typedef long long ll;
const ll N = 100007;
ll inv[N] = {0, 1};
int main() {
	for(int i=2; i<=N; i++) {
		inv[i] = -(N/i) * inv[N%i];//递推关系
		inv[i] = (inv[i] % N + N) % N;//确保inv[i] > 0;
	} 
	for(int i=0; i<100; i++) {
		cout << inv[i] << ' ';
	} 
	return 0;
} 

你可能感兴趣的:(数论,笔记)