数论 · 逆元

前言

针对比赛学的一点点逆元,在这里记录一下其中一种方法。

当然求逆元的方法有很多种,之后学到再回来写。 —— 2021.8.24

UPDATE

  • 2021 - 11 - 25 填坑。

    添加了两种求逆元的方法 + 修改了一些写得不好(难看至极)的地方

  • 2021 - 12 - 01 补充了欧拉定理的证明。

定义

a x ≡ 1 ( m o d b ) ax \equiv 1\pmod b ax1(modb) a ⊥ b a\perp b ab(a,b 互质),

则称 x x x a a a 的逆元。

一、费马小定理 + 快速幂

若 p 为质数,且 q ⊥ p q \perp p qp,根据费马小定理,有 q p − 1 ≡ 1 ( m o d p ) q^{p-1} \equiv 1 \pmod {p} qp11(modp)

带入原柿子中就有:

∵ a x ≡ 1 ≡ a p − 1 ( m o d p ) \because ax \equiv 1 \equiv a^{p-1} \pmod p ax1ap1(modp)

∴ x ≡ a p − 2 ( m o d p ) \therefore x \equiv a^{p-2} \pmod p xap2(modp)

然后直接用快速幂求解即可。

代码

省略快速幂。

inline int fermat (int a, int n)
{
	return pow_mod (a, n - 2, n);
}


补:费马小定理证明

费马小定理实际上就是 欧拉定理(证明) 的一个特殊情况。

我们定义 φ x \varphi_{x} φx 为从 1 到 x 和 x 互质的整数的数量。

例如: φ 8 = 4 \varphi_{8}=4 φ8=4,分别是:1、3、5、7。

欧拉定理就是: x φ p ≡ 1 ( m o d p ) x^{\varphi_p} \equiv 1 \pmod p xφp1(modp)

而费马小定理就是当 p 为质数时的一个特殊情况 。

易证,当 p 为质数时, φ p = p − 1 \varphi_p=p-1 φp=p1

所以当 p 为质数时, x p − 1 ≡ 1 ( m o d p ) x^{p-1} \equiv 1 \pmod p xp11(modp)



二、扩展欧几里得

∵ a x ≡ 1 ( m o d p ) ( x 为逆元 ) \because ax \equiv 1 \pmod p (\text{x 为逆元}) ax1(modp)(为逆元)

∴ a x + b y = 1 \therefore ax + by=1 ax+by=1

这样就可以直接用扩欧求逆元了。

但对于一次性求许多逆元(如洛谷的模板),效率并不高。

数论·扩欧

代码

省略扩欧代码。

inline int exgcd_inv (int a, int n)
{
	int gcd, x, y;
	gcd = exgcd (a, n);//求解方程 ax + ny = gcd (a, n)
	return d == 1 ? (x + n) % n : -1;
}

三、线性求逆元

首先,有 1 − 1 ≡ 1 ( m o d p ) 1^{-1} \equiv 1\pmod p 111(modp)

再设 p = k ∗ i + r ,   r < i ,   1 < i < p p=k*i+r,\ r<i,\ 1<i<p p=ki+r, r<i, 1<i<p

这样就有 k ∗ i + r ≡ 0 ( m o d p ) k * i + r \equiv 0 \pmod p ki+r0(modp),然后给两边同乘上 i i i 的逆元和 r r r 的逆元,

可化简得到: k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k * r^{-1} + i ^{-1} \equiv 0 \pmod p kr1+i10(modp)

易知: k = [ p i ] ∗ ,   r = p   m o d   i k=\left[\dfrac{p}{i}\right] * ,\ r = p \bmod i k=[ip], r=pmodi

带入上柿可得:

i − 1 ≡ − [ p i ] ∗ ( p   m o d   i ) − 1 ( m o d p ) i^{-1} \equiv -\left[\dfrac{p}{i}\right] * (p \bmod i)^{-1} \pmod p i1[ip](pmodi)1(modp)

这样就可以用递推求出逆元了。

代码

建数组 i n v [ ] inv[] inv[] i n v i inv_i invi 表示 i i i 的逆元。就有:

inv[i] = -(p / i) * inv[p % i];

用这个方法过了模板题的代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rint register int
const int maxn = 3e6 + 5;
int inv[maxn], n, q;

signed main ()
{
	scanf ("%lld %lld", &n, &q);
	inv[1] = 1;
	printf ("1\n");
	for (rint i (2); i <= n; ++i)
	{
		inv[i] = ((-(q / i) * inv[q % i]) % q + q) % q;
		printf ("%lld\n", inv[i]);
	}
	return 0;	
} 

—— E n d End End——

posted @   pldzy  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示