学习笔记:同余

同余

定义

设整数 m0。若 m(ab),称 m 为模数(模),a 同余于 bmba 对模 m 的剩余。记作 ab(modm)

否则,a 不同余于 bmb 不是 a 对模 m 的剩余。记作 ab(modm)

这样的等式,称为模 m 的同余式,简称同余式。

根据整除的性质,上述同余式也等价于 ab(mod(m))

如果没有特别说明,模数总是正整数。

式中的 ba 对模 m 的剩余,这个概念与余数完全一致。通过限定 b 的范围,相应的有 a 对模 m 的最小非负剩余、绝对最小剩余、最小正剩余。

性质

  • 自反性:aa(modm)
  • 对称性:若 ab(modm),则 ba(modm)
  • 传递性:若 ab(modm),bc(modm),则 ac(modm)
  • 线性运算:若 a,b,c,dZ,mN,ab(modm),cd(modm) 则有:
    • a±cb±d(modm)
    • a×cb×d(modm)
  • a,bZ,k,mN,ab(modm), 则 akbk(modmk)
  • a,bZ,d,mN,da,db,dm,则当 ab(modm) 成立时,有 adbd(modmd)
  • a,bZ,d,mN,dm,则当 ab(modm) 成立时,有 ab(modd)
  • a,bZ,d,mN,则当 ab(modm) 成立时,有 gcd(a,m)=gcd(b,m)。若 d 能整除 ma,b 中的一个,则 d 必定能整除 a,b 中的另一个。

应用

扩展欧几里得算法

扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求 ax+by=gcd(a,b) 的一组可行解。

过程

ax1+by1=gcd(a,b)bx2+(amodb)y2=gcd(b,amodb)

由欧几里得定理可知:gcd(a,b)=gcd(b,amodb)

所以 ax1+by1=bx2+(amodb)y2

又因为 amodb=a(ab×b)

所以 ax1+by1=bx2+(a(ab×b))y2

ax1+by1=ay2+bx2ab×by2=ay2+b(x2aby2)

因为 a=a,b=b,所以 x1=y2,y1=x2aby2

x2,y2 不断代入递归求解直至 gcd(最大公约数,下同)为 0 递归 x = 1, y = 0 回去求解。

实现

int exgcd(int a, int b, int &x, int &y){
    if(b == 0){x = 1;y = 0;return b;}
    else{
        int d = exgcd(b, a % b, y, x);
        y -= a / b * x;return d;
    }
}

函数返回的值为 gcd,在这个过程中计算 x,y 即可。

值域分析

ax+by=gcd(a,b) 的解有无数个,显然其中有的解会爆 long long
万幸的是,若 b0,扩展欧几里得算法求出的可行解必有 |x|b,|y|a,下面给出这一性质的证明。

证明:

gcd(a,b)=b 时,amodb=0,必在下一层终止递归。
得到 x1=0,y1=1,显然 a,b1|x1|,|y1|
gcd(a,b)b 时,设 |x2|(amodb),|y2|b
因为 x1=y2,y1=x2aby2
所以 |x1|=|y2|b,|y1||x2|+|aby2|(amodb)+ab|y2|
aabb+ab|y2|aab(b|y2|)
amodb=aabbaab(b|y2|)a
因此 |x1|b,|y1|a 成立。

证毕。

中国剩余定理

引入

「物不知数」问题:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

即求满足以下条件的整数:除以 32,除以 53,除以 72

该问题最早见于《孙子算经》中,并有该问题的具体解法。宋朝数学家秦九韶于 1247 年《数书九章》卷一、二《大衍类》对「物不知数」问题做出了完整系统的解答。上面具体问题的解答口诀由明朝数学家程大位在《算法统宗》中给出:

三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知。

2×70+3×21+2×15=233=2×105+23,故答案为 23

定义

中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中 n1,n2,,nk 两两互质):

{xa1(modn1)xa2(modn2)xak(modnk)

上面的「物不知数」问题就是一元线性同余方程组的一个实例。

过程

  1. 计算所有模数的积 n
  2. 对于第 i 个方程:
    1. 计算 mi=nni
    2. 计算 mi 在模 ni 意义下的逆元 mi1
    3. 计算 ci=mimi1不要对 ni 取模)。
  3. 方程组在模 n 意义下的唯一解为:x=i=1kaici(modn)

实现

洛谷 P1495【模板】中国剩余定理(CRT)/ 曹冲养猪

题目描述

自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有 16 头母猪,如果建了 3 个猪圈,剩下 1 头猪就没有地方安家了。如果建造了 5 个猪圈,但是仍然有 1 头猪没有地方去,然后如果建造了 7 个猪圈,还有 2 头没有地方去。你作为曹总的私人秘书理所当然要将准确的猪数报给曹总,你该怎么办?

输入格式

第一行包含一个整数 n —— 建立猪圈的次数,接下来 n 行,每行两个整数 ai,bi,表示建立了 ai 个猪圈,有 bi 头猪没有去处。你可以假定 a1an 互质。

输出格式

输出包含一个正整数,即为曹冲至少养母猪的数目。

#include <iostream>
#define int long long
using namespace std;
int n, a[15], b[15];
int p = 1, t = 0;
int exgcd(int a, int b, int &x, int &y){
    if(b == 0){x = 1;y = 0;return a;}
    else{
        int d = exgcd(b, a % b, y, x);
        y -= a / b * x;return d;
    }
}
int inv(int a, int p){
    int x, y;exgcd(a, p, x, y);
    return (x % p + p) % p;
}
signed main(){
    ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
    cin >> n;
    for(int i = 1 ; i <= n ; i ++)
        cin >> a[i] >> b[i];
    for(int i = 1 ; i <= n ; i ++)p *= a[i];
    for(int i = 1 ; i <= n ; i ++)
        t += (b[i] * p / a[i] * inv(p / a[i], a[i])) % p;
	cout << t % p << endl;return 0;
}

证明

我们需要证明上面算法计算所得的 x 对于任意 i=1,2,,k 满足 xai(modni)

ij 时,有 mj0(modni),故 cjmj0(modni)。又有 cimi(mi1modni)1(modni),所以我们有:

xj=1kajcj(modni)aici(modni)aimi(mi1modni)(modni)ai(modni)

即对于任意 i=1,2,,k,上面算法得到的 x 总是满足 xai(modni),即证明了解同余方程组的算法的正确性。

因为我们没有对输入的 ai 作特殊限制,所以任何一组输入 {ai} 都对应一个解 x

另外,若 xy,则总存在 i 使得 xy 在模 ni 下不同余。

故系数列表 {ai} 与解 x 之间是一一映射关系,方程组总是有唯一解。

证毕。

解释

下面演示 CRT 如何解「物不知数」问题。

  1. n=3×5×7=105
  2. 三人同行 七十 希:n1=3,m1=n/n1=35,m112(mod3),故 c1=35×2=70
  3. 五树梅花 廿一 支:n2=5,m2=n/n2=21,m211(mod5),故 c2=21×1=21
  4. 七子团圆正 半月n3=7,m3=n/n3=15,m311(mod7),故 c3=15×1=15
  5. 所以方程组的唯一解为 x2×70+3×21+2×1523323(mod105)。(除 百零五 便得知)

应用

某些计数问题或数论问题出于加长代码、增加难度、或者是一些其他原因,给出的模数:不是质数

但是对其质因数分解会发现它没有平方因子,也就是该模数是由一些不重复的质数相乘得到。

那么我们可以分别对这些模数进行计算,最后用 CRT 合并答案。

扩展中国剩余定理:模数不互质的情况

两个方程

设两个方程分别是 xa1(modm1)xa2(modm2)

将它们转化为不定方程:x=m1p+a1=m2q+a2,其中 p,q 是整数,则有 m1pm2q=a2a1

由裴蜀定理可知,当 a2a1 不能被 gcd(m1,m2) 整除时,无解;

其他情况下,可以通过扩展欧几里得算法解出来一组可行解 (p,q)

则原来的两方程组成的模方程组的解为 xb(modM),其中 b=m1p+a1M=lcm(m1,m2)

多个方程

用上面的方法两两合并即可。

洛谷 P4777【模板】扩展中国剩余定理(EXCRT)

题目描述

给定 n 组非负整数 ai,bi ,求解关于 x 的方程组的最小非负整数解。

{xb1 (mod a1)xb2 (mod a2)...xbn (mod an)

输入格式

输入第一行包含整数 n

接下来 n 行,每行两个非负整数 ai,bi

输出格式

输出一行,为满足条件的最小非负整数 x

#include <iostream>
#define int __int128
#define MAXN 100005
using namespace std;
int x, y, d, n;
int a, b, A, B;
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void write(int x){
    if(x < 0){putchar('-');x = -x;}
    if(x >= 10)write(x / 10);
    putchar(x % 10 ^ 48);
}
void exgcd(int a, int b, int &x, int &y){
    if(b == 0){d = a;x = 1;y = 0;
    }else{
        exgcd(b, a % b, y, x);
        y -= a / b * x;
    }
}
int gcd(int a, int b){return b ? gcd(b, a % b) : a;}
int lcm(int a, int b){return a / gcd(a, b) * b;}
void merge(){
    exgcd(a, A, x, y);int c = B - b;
    if(c % d){puts("-1");exit(0);}
    x = x * c / d % (A / d);
    if(x < 0)x += A / d;
    int mod = lcm(a, A);
    b = (a * x + b) % mod;
    if(b < 0)b += mod;
    a = mod;
}
signed main(){
    n = read();
    for(int i = 1 ; i <= n ; i ++){
        int _A, _B;
        _A = read();_B = read();
        A = _A;B = _B;
        if(i > 1)merge();
        else{a = A;b = B;}
    }
    write(b % a);putchar('\n');return 0;
}
posted @   tsqtsqtsq  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示