RE:从 0 开始的幼儿园数论生活

你猜为什么我数学那么差?

1. 从欧几里得算法到扩展欧几里得算法

我们一般用欧几里得算法求最大公约数,它差不多就这样

gcd(m,n)={nm=0gcd(n,mmodn)(m0)

扩欧可以用来求这个:ax+by=gcd(a,b) 的正整数解 x,y。我们用欧几里得算法来迭代求解,我们得出来的解满足 |x|+|y| 最小。

b=0 时,gcd(a,b)=a,因此 x=1,y=0,其实 y 可以是任意一个数字(因为 b=0),但是由于我们要求的是 |x|+|y| 最小的解,所以 y=0

接下来假设我们已经求出来了 bx+(amodb)y=gcd(a,b),从这一步再求 ax+by=gcd(a,b) 的解(相当于递归回来做),amodb=aabb,所以就有
bx+(amodb)y=bx+(aabb)y=ay+b(xaby),也就是说 x=y,y=xaby

这个玩意儿叫 exgcd


2. 二元一次方程的通解

也就是求 ax+by=c 这样的方程,通过上面的讨论我们知道只有 gcd(a,b)|c 这样的方程才有整数解,为了方便叙述下面记 d=gcd(a,b)

首先用 exgcd 求出 ax+by=d 的一对解 x,y,然后容易得到原方程 ax+by=c 的通解形式是 {x=xcd+kbdy=ycdkad。记 cd=g,那么 {x=xg+kbdy=ygkad 这样写就好看很多了(雾)

下面让我们来做这道题吧。

Task1 正整数解数量

就是求 {xg+kbd1ygkad1 这个不等式组的整数解数量。不难解出来是 (1xg)dbk(yg1)da。由于是整数,所以左边套上向上取整,右边套上向下取整,就得到了 (1xg)dbk(yg1)da。知道取值范围这个就好做了!

Task2 求最大最小解

不难想到 x 越大 y 越小,反之亦然,所以 k=(1xg)dbx 最小 y 最大,k=(yg1)dax 最大 y 最小。

喜闻乐见的代码

//SIXIANG
#include <iostream>
#include <cmath>
#define MAXN 100000
#define int long long
#define QWQ cout << "QWQ" << endl;
using namespace std;
int exgcd(int a, int b, int &x, int &y) {
	if(!b) {x = 1, y = 0; return a;}
	else {
		int rest = exgcd(b, a % b, x, y);
		int tmp = x; x = y, y = tmp - a / b * y;
		return rest;
	}
} 
signed main() {
	int T, a, b, c; cin >> T;
	while(T--) {
		cin >> a >> b >> c;
		
		int x, y, d = exgcd(a, b, x, y);
		if(c % d != 0) {
			cout << -1 << endl;
			continue;
		}
		int g = c / d;
		int L = ceil((double)(1.0 - x * g) * d / (double)(b)), R = floor((double)(y * g - 1.0) * d / (double)(a));
		if(L > R)
			cout << x * g + L * b / d << ' ' << y * g - R * a / d << endl;
		else {
			cout << (R - L + 1) << ' ' << x * g + L * b / d << ' ' << y * g - R * a / d << ' ' << x * g + R * b / d<< ' ' << y * g - L * a / d<< endl;
		}
	}
}

3. 同余与一些关于它的定义

同余就是 ab(modm),它们表示 ab 除以 m 的余数相同,炒个例子,61 关于 5 同余,就可以写成 61(mod5)

如果 ab(modm),那么 (a+c)(b+c)(modm)acbc(modm)

同余有如下性质:(a+b)(amodm+bmodm)(modm)(ab)(amodmbmodm)(modm)(a×b)(amodm×bmodm)(modm)

除法就没有这样的性质了。我们要单独讨论它,方法是求逆元,还要用上别的知识,一般用 exgcd/费马小定理求逆元,还有线性的方法(不过好像用得不多?)

剩余系:模 n 的完全剩余系是一个集合 Zn,这个集合是 Zn={0,1,2,3,,n1}。这里面的元素不是普通的元素,这里面的每个数代表所有与它同余的整数。举个例子,Z7,这里面的 6 元素代表的是 6,13,20

简化剩余系(也叫缩系):将 Zn 里面只保留与 n 互质的数,记为 Zn(可能不是很规范 qwq)比如 Z8={1,3,5,7}

由于它们两个非常的与众不同,所以它们的运算也很与众不同,比如 Z53+4=2((3+4)2(mod5)),3×5=0((35)0(mod5))

特别的,在简化剩余系里面乘法封闭,意思就是说这里面做乘法的结果还在原来的集合里面,比如 Z8 里面 3×5=7 本来就在里面。这个很好证明,如果 an 互质,bn 互质,那么 abn 互质。gcd(ab,n)=1,根据欧几里得算法所以也有 gcd(ab,n)=gcd(n,abmodn)=1


4. 欧拉函数的定义

先写一个定义 qwq

请不要认为懂了欧拉函数的定义就啥都明白了,欧拉函数的精髓并不在于它的定义和公式,这里提及完全是为了证欧拉定理费马小定理算逆元(

欧拉函数 φ(n) 定义为 1n 中与 n 互质的数个数。比如 φ(5)=4

我们记 n 的质因数分别为 p1,p2,,pkφ(n)=n(11p1)(11p2)(11pk)

证明吗?展开式子,然后就会发现这玩意儿实际上是个容斥,乱搞就能整出来。

这样就可以在 O(n) 的时间复杂度内求出单个 φ(n)


5. 欧拉定理和费马小定理

其实欧拉函数不应该放在这么前面写的,放在这么前面是为了写一写欧拉定理和费马小定理。

先介绍欧拉定理:aφ(m)1(modm),其中 a,m 互质。

记得当时看这个的证明一脸懵逼,现在看上去还是很简单的 23333

证明:对于 m 的简化剩余系 Zn={x1,x2,,xφ(m)},每个元素同乘一个 a 得到 aZn={ax1,ax2,,axφ(m)}

由于 am 互质,所以从简化剩余系的角度来看,这两个集合是等价的,所以我们容易得到 i=1φ(m)xii=1φ(m)axi(modm),也就是 i=1φ(m)xii=1φ(m)xiaφ(m)(modm),所以 aφ(m)1(modm)

感觉这个的证明相当优美啊 qwq!

费马小定理:ap11(modp)p 是质数。

众所周知如果 p 是质数那么 φ(p)=p1。所以其实费马小定理就是欧拉定理的一个推论。

费马小定理用途很广,不仅可以用来做同余,最常见的用途是用来求逆元。


6. 逆元

有的时候我们需要求 abmodm 的结果,这个时候就不能用常规的方法做了。

逆元可以做这个东西,找到一个 x,使得 abx(modm)

顺带提一嘴,这里的 a,b 都是剩余系意义下的,比如说 37mod5,你会说诶诶诶这压根不是整数它怎么取余再这样下去得输越南,但是实际上这里的 3 是一个除以 53 的数,7 同理,这个时候就能整除了。

其实,我们只需要求出一个 1bx(modm)x,那么 ab 就是两边同乘 a 的结果,这个 x 就叫做 b 在模 m 意义下的逆元,记作 b1a/b 就是 a×b1

它怎么求?

  1. exgcd
    转化一下 xb1(modm),线性同余方程组,那 exgcd 随便跑。有解情况是 b,m 互质。这是一个线性同余方程,具体解法下面会提及。
  2. 费马小定理
    只适用于 m 为质数,由于 ap11(modm),所以 a×ap21(modm)。所以 a1=ap2
  3. 线性递推。
    这个就很高级,虽然我觉得它似乎没什么用,因为 OI 里面我们都知道 O(n) O(nlog2n) 众生平等(划掉
    但是学一手以防万一对吧()
    众所周知,11=1。我们记 ki+r=m,那么就有
    ki+r0(modm)。两边同乘 i1 得到
    k+ri10(modm)i1kr1(modm),即 i1pi(pmodi)1(modm)

有了逆元我们就可以做模意义下的除法辣✿✿ヽ(°▽°)ノ✿


7. 线性同余方程

axb(modm),形如这样的关于 x 的方程。

脑子告诉我们它等价于这样一个式子 axkm=b,拿 exgcd 一跑就行了.


8. 线性同余方程组,模数互质(CRT)

这就是我们幼儿园学习过的韩信点兵问题,但是当时没有学一个一般化(雾)的解法。

它是用来解形如 {xr1(modm1)xr2(modm2)xrn(modmn)。其中 m1mn 两两互质。

我们记 M=i=1nm1vi=(Mmi)1(mi),通解形式是 ans=i=1nMmiviri+kM,最小解是 ans=i=1nMmivirimodM

证明如下:先指针对一个同余方程 xri(modmi) 看,计算它的贡献,为了消去 x 对其它方程组的影响,我们先给它乘上一个 Mmi。由于我们答案是求和的,这样做它对其它方程组取余的结果就为 0,不会影响答案。

我们令 xMmiri(modmi),移项得到 xrimiM(modmi),也就是 xri×(Mmi)1(modmi),这里对答案的贡献就是 Mmiviri

CRT 这样的“乘一个倍数消去贡献”的方法很好用,很多题都能用的上,同时对于许多模数有严格限制(比如 NTT)这样的算法,我们可以先拿两个大质数算出解,构造个同余方程组,然后对于新模数再计算,这样常数固然会比较大,但是相对于 MTT(显而易见我没学过它)好理解十倍甚至九倍(划掉

逆元拿 exgcd 算,代码都是老早之前写的,明天重写一份贴上来 qwq


9. 线性同余方程组,模数不一定互质(exCRT)

它还是用来解 {xr1(modm1)xr2(modm2)xrn(modmn) 这样的同余方程组,但不同的是这时候 m1,m2,,mn 不一定两两互质。

exCRT 其实和 CRT 真的什么关系都没有了,它的思想是合并同余方程。假设我们已经求出来前 k1 个方程的一个解 x,下面我们希望 x 能够写成 x+a 的形式,使得 xrk(modmk)

和 CRT 一样,为了不让我们加上的数字对前面的方程产生影响,我们选择加上一个 lcm(m1,m2,m3,,mk1) 的倍数,记 M=lcm(m1,m2,m3,,mk1),那么我们就可以把我们要构造出来的 x 写成 x+tM 的形式。此时 x+tMrk(modmk)。移项得到 tMrkx(modmk)。用 exgcd 解出来这个线性同余方程,我们就可以得到一个 t,最后 x=x+tM

根据数学归纳法,发现了没有:我们解出来了!

代码也明天填坑 qwq


10. 离散对数(底数和模数互质,BSGS)

BSGS 用来求这样的方程 axb(modm) 的解,其中 a,m 互质。

根据欧拉定理,我们知道 aφ(m)11(modm),同时很显然然 a01(modm)φ(m)<m,也就是说暴力的话只要枚举 x1m 范围内就好了。

当然咯,这样太慢了,有一种很神奇的做法,叫 BSGS(Shank's Baby-Step-Giant-Step),大步小步,它的名字很形象生动。首先我们选定一个 k 先暴力计算出来 aimodm 的值,其中 1ik。我们记 vi=aimodm

对于 i>k,那么我们就把 aib(modm) 写成一个 aj×atkb(modm)(j<k),移项得到 ajb×atk(modm)ajmodm 的结果我们已经算过了,只需要用一个哈希表存着就行了,atk 的逆元每次迭代完算。

哈希表我们用 map 存,所以会加一只 log。

很显然时间复杂度 O((k+m/k)log2m),很显然 k=(m) 的时候,这个式子最小,此时时间复杂度 O((m)log2m)

感觉这个算法好可爱鸭,看这个名字想到有一个叫 shank 的大人牵着一个小孩子学走路 qwq(划掉


写这篇文章可以看做是梳理一下我学过的幼儿园数论知识们,也方便我复习之类的 qwq。听说还有什么 Min25 筛鸭,洲阁筛鸭这些很高级的东西,但是我是幼儿园小朋友,所以暂时用不到那么高级的东西。

posted @   SIXIANG32  阅读(211)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示