本系列文章将于2021年整理出版。前驱教材:《算法竞赛入门到进阶》 清华大学出版社
网购:京东 当当 作者签名书:点我
公众号同步:算法专辑
暑假福利:胡说三国
有建议请加QQ 群:567554289
文章目录
同余是很巧妙的工具,它使得人们能够用等式的形式来简洁地描述整除关系。
在阅读本节内容时,请对照上一节“线性丢番图方程”的内容,有很多类似的地方。
1. 同余概述
1.1. 同余定义
设m是正整数,若a和b是整数,且
m
∣
(
a
−
b
)
m | (a - b)
m∣(a−b),则称
a
a
a和
b
b
b模
m
m
m同余。也就是说,
a
a
a除以
m
m
m得到的余数,和
b
b
b除以
m
m
m的余数相同;或者说,
a
−
b
a - b
a−b除以
m
m
m,余数是0。
把
a
a
a和
b
b
b模
m
m
m同余记为
a
≡
b
(
m
o
d
m
)
a\equiv b (mod m)
a≡b(modm),
m
m
m称为同余的模。例子:
(1)因为7|(18-4),所以18
≡
\equiv
≡ 4 (mod 7),18除以7余数是4,4除以7的余数也是4;
(2)3
≡
\equiv
≡ 6 (mod 9),3除以9余数是3,-6除以9的余数也是3;
(3)13和5模9不同余,因为13除以9余数是4,5除以9余数是5。
1.2. 一些定理和性质
(1)若
a
a
a和
b
b
b是整数,
m
m
m为正整数,则
a
≡
b
(
m
o
d
m
)
a\equiv b (mod\ m)
a≡b(mod m)当且仅当
a
m
o
d
m
=
b
m
o
d
m
a\ mod\ m = b mod\ m
a mod m=bmod m。
(2)把同余式转换为等式。若
a
a
a和
b
b
b是整数,则
a
≡
b
(
m
o
d
m
)
a\equiv b (mod\ m)
a≡b(mod m)当且仅当存在整数,使得
a
=
b
+
k
m
a = b + km
a=b+km。例如:19-2 (mod 7),有19 = -2 + 3 ∙ 7。
(3)设
m
m
m是正整数,模
m
m
m的同余满足下面的性质:
1)自反性。若
a
a
a是整数,则
a
≡
a
(
m
o
d
m
)
a\equiv a (mod\ m)
a≡a(mod m)
2)对称性。若
a
a
a和
b
b
b是整数,且
a
≡
b
(
m
o
d
m
)
a\equiv b (mod\ m)
a≡b(mod m),则
b
≡
a
(
m
o
d
m
)
b\equiv a (mod\ m)
b≡a(mod m)。
3)传递性。若
a
、
b
、
c
a、b、c
a、b、c是整数,且
a
≡
b
(
m
o
d
m
)
和
b
≡
c
(
m
o
d
m
)
a\equiv b (mod\ m)和b\equiv c (mod m)
a≡b(mod m)和b≡c(modm),则
a
≡
c
(
m
o
d
m
)
a\equiv c (mod\ m)
a≡c(mod m)。
2. 一元线性同余方程
一元线性同余方程:设
x
x
x是未知数,给定
a
、
b
、
m
a、b、m
a、b、m,求整数
x
x
x,满足
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)。
研究线性同余方程有什么用处?
a
x
≡
b
(
m
o
d
m
)
ax\equiv b(mod\ m)
ax≡b(mod m)表示
a
x
−
b
ax - b
ax−b是
m
m
m的倍数,设为
−
y
-y
−y倍,则有
a
x
+
m
y
=
b
ax + my = b
ax+my=b,这就是二元线性丢番图方程。所以,求解一元线性同余方程等同于求解二元线性丢番图方程。
方程是否有解?如果有解,有多少解?如何求出解?与线性丢番图的定理一样,线性同余方程也有类似的定理。
定理:设
a
,
b
a,b
a,b和
m
m
m是整数,
m
>
0
,
g
c
d
(
a
,
m
)
=
d
m > 0,gcd(a, m) = d
m>0,gcd(a,m)=d,若
d
d
d不能整除b,则
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)无解,若
d
d
d能整除
b
b
b,则
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)有
d
d
d个模
m
m
m不同余的解。
定理的前半部分可以概况为:
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)有解的充分必要条件是
g
c
d
(
a
,
m
)
gcd(a, m)
gcd(a,m)能整除b。
定理的后半部分说明了解的情况。与线性丢番图方程类似,如果有一个特解是
x
0
x_0
x0 ,那么通解是
x
=
x
0
+
(
m
/
d
)
n
x = x_0 + (m/d)n
x=x0+(m/d)n,当
n
=
0
,
1
,
2
,
.
.
.
,
d
−
1
n = 0, 1, 2, ..., d -1
n=0,1,2,...,d−1时,有
d
d
d个模
m
m
m不同余的解。
推论:
a
a
a和
m
m
m互素时,因为
d
=
g
c
d
(
a
,
m
)
=
1
d = gcd(a, m)=1
d=gcd(a,m)=1,所以线性同余方程
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)有唯一的模
m
m
m不同余的解。这个推论在下一节的逆中有应用。
最后回到求解
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)的问题:首先求逆,然后利用逆求得
x
x
x。
3. 逆
求解一般形式的同余方程 a x ≡ b ( m o d m ) ax\equiv b (mod\ m) ax≡b(mod m),需要用到逆。
3.1.逆的概念
给定整数a,且满足
g
c
d
(
a
,
m
)
=
1
gcd(a, m) = 1
gcd(a,m)=1,称
a
x
≡
1
(
m
o
d
m
)
ax\equiv 1(mod\ m)
ax≡1(mod m)的一个解为
a
a
a模
m
m
m的逆。记为
a
−
1
a^{-1}
a−1。
例如:
8
x
≡
1
(
m
o
d
31
)
8x\equiv 1(mod\ 31)
8x≡1(mod 31),有一个解是
x
x
x = 4,4是8模31的逆。所有的解,例如35、66等,都是8模31的逆。
可以借助丢番图方程理解逆的概念,
8
x
≡
1
(
m
o
d
31
)
8x\equiv 1(mod\ 31)
8x≡1(mod 31)即方程
8
x
+
31
y
=
1
8x + 31y = 1
8x+31y=1,
x
x
x = 4是8模31的逆,4×8-1能整除31。
3.2.求逆
有多种方法可以求逆。
(1)扩展欧几里得求单个逆
下面的例题是求逆,即求解同余方程
a
x
≡
1
(
m
o
d
m
)
ax\equiv 1(mod\ m)
ax≡1(mod m)。
同余方程(求逆) 洛谷 P1082
题目描述:求关于x的同余方程
a
x
≡
1
(
m
o
d
m
)
ax\equiv 1(mod\ m)
ax≡1(mod m)的最小正整数解。2≤a, m≤2,000,000,000。
题解: a x ≡ 1 ( m o d m ) ax\equiv 1(mod\ m) ax≡1(mod m),即丢番图方程 a x + m y = 1 ax + my = 1 ax+my=1,先用扩展欧几里得求出 a x + m y = 1 ax + my = 1 ax+my=1的一个特解 x 0 x_0 x0,通解是 x = x 0 + m n x = x_0 + mn x=x0+mn。然后通过取模操作算最小整数解 ( ( x 0 m o d m ) + m ) m o d m ( (x_0 mod\ m) + m) mod\ m ((x0mod m)+m)mod m,因为 m m m > 0,可以保证结果是正整数。
long long mod_inverse(long long a, long long m){ //求逆
long long x,y;
extend_gcd(a,m,x,y);
return (x%m + m) % m; //保证返回最小正整数
}
int main(){
long long a,m; cin >> a >>m;
cout << mod_inverse(a,m);
return 0;
}
(2)费马小定理求单个逆
费马小定理:设
n
n
n是素数,
a
a
a是正整数且与
n
n
n互质,那么有
a
n
−
1
≡
1
(
m
o
d
n
)
a^{n-1}\equiv 1(mod\ n)
an−1≡1(mod n)。
a
a
n
−
2
≡
1
(
m
o
d
n
)
a a^{n-2}\equiv 1(mod\ n)
aan−2≡1(mod n),那么
a
n
−
2
m
o
d
n
a^{n-2} mod\ n
an−2mod n就是
a
a
a模
n
n
n的逆。计算需要用到快速幂取模fast_pow(),参考前面章节“大素数的判定”。快速幂取模的复杂度是
O
(
l
o
g
n
)
O(log n)
O(logn)的。
long long mod_inverse(long long a,long long mod){
return fast_pow(a,mod - 2,mod);
}
(3)递推求多个逆
如果要求1 ~ n内所有的逆,可以用递推。复杂度是O(n)的。
乘法逆元 洛谷 P3811
题目描述:给定
n
,
p
n,p
n,p,求
1
n
1 ~ n
1 n 中所有整数在模
p
p
p 意义下的乘法逆元。
1
≤
n
≤
3
×
1
0
6
1≤n≤3×10^6
1≤n≤3×106,
n
<
p
<
20000528
n < p < 20000528
n<p<20000528,
p
p
p为质数。
输入:一行两个正整数
n
、
p
n、p
n、p。
输出:输出
n
n
n行,第
i
i
i行表示
i
i
i在模
p
p
p下的乘法逆元。
首先,
i
=
1
i=1
i=1时逆是1。下面求
i
>
1
i > 1
i>1时的逆,用递推法。
设
p
/
i
=
k
p/i = k
p/i=k,余数是
r
r
r,即
k
i
+
r
≡
0
(
m
o
d
p
)
k i + r\equiv 0(mod\ p)
ki+r≡0(mod p);
在两边乘
i
−
1
r
−
1
i^{-1} r^{-1}
i−1r−1,得到
k
r
−
1
+
i
−
1
≡
0
(
m
o
d
p
)
k r^{-1} + i^{-1}\equiv 0 (mod\ p)
kr−1+i−1≡0(mod p);
移项得
i
−
1
≡
−
k
r
−
1
(
m
o
d
p
)
i^{-1}\equiv - k r^{-1} (mod\ p)
i−1≡−kr−1(mod p) ,即
i
−
1
≡
−
p
/
i
r
−
1
(
m
o
d
p
)
i^{-1}\equiv - p/i r^{-1} (mod\ p)
i−1≡−p/ir−1(mod p) ,
i
−
1
≡
(
p
−
p
/
i
)
r
−
1
(
m
o
d
p
)
i^{-1}\equiv (p - p/i) r^{-1} (mod\ p)
i−1≡(p−p/i)r−1(mod p)。
long long inv[maxn];
void inverse(long long n, long long p){
inv[1]=1;
for(int i = 2;i<maxn;i++)
inv[i]= (p - p/i) * inv[p%i] % p;
}
下面给出一个求逆的例题。
A/B hdu 1576
题目描述:求(A/B)%9973,但由于A很大,我们只给出n (n = A%9973)(我们给定的A必能被B整除,且gcd(B, 9973) = 1)。
输入:第一行是T,表示有T组数据。每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。
输出:对每组数据,输出(A/B)%9973。
题解:
设答案
k
=
(
A
/
B
)
k = (A/B) % 9973
k=(A/B)。
做以下变换:
A
/
B
=
k
+
9973
x
,
A
=
k
B
+
9973
x
B
A/B = k + 9973 x,A = kB + 9973 x B
A/B=k+9973x,A=kB+9973xB;
把
A
A % 9973 = n
A代入得
k
B
k B % 9973 = n
kB,即
k
B
=
n
+
9973
y
k B = n + 9973y
kB=n+9973y;
两边除
n
n
n得
(
k
/
n
)
B
+
(
−
y
/
n
)
9973
=
1
(k/n) B + (-y/n) 9973 = 1
(k/n)B+(−y/n)9973=1,这是形如
a
x
+
b
y
=
1
ax + by = 1
ax+by=1的丢番图方程,即
a
x
≡
1
(
m
o
d
m
)
ax\equiv 1(mod\ m)
ax≡1(mod m),其中
x
=
k
/
n
x = k/n
x=k/n。求解逆
x
x
x,得到
k
/
n
k/n
k/n,再乘以
n
n
n,就是
k
k
k。
3.3. 用逆求解同余方程
逆有什么用?如果有
a
a
a模
m
m
m的一个逆,可以用来解如
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)的任何同余方程。
记
a
−
1
a^{-1}
a−1是
a
a
a的一个逆,有
a
−
1
a
≡
1
(
m
o
d
m
)
a^{-1}a\equiv 1(mod\ m)
a−1a≡1(mod m)。在
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)的两边同时乘以
a
−
1
a^{-1}
a−1,得到
a
−
1
a
x
≡
a
−
1
b
(
m
o
d
m
)
a^{-1}ax\equiv a^{-1}b(mod\ m)
a−1ax≡a−1b(mod m),即
x
≡
a
−
1
b
(
m
o
d
m
)
x\equiv a^{-1}b (mod\ m)
x≡a−1b(mod m)。
例如:为了求出
8
x
≡
22
(
m
o
d
31
)
8x\equiv 22 (mod\ 31)
8x≡22(mod 31)的解,可以两边乘以4,4是8模31的一个逆,得
4
∗
8
x
=
4
∗
22
(
m
o
d
31
)
4 * 8x = 4 * 22 (mod\ 31)
4∗8x=4∗22(mod 31),因此
x
≡
88
(
m
o
d
31
)
≡
26
(
m
o
d
31
)
x\equiv 88 (mod\ 31)\equiv 26 (mod\ 31)
x≡88(mod 31)≡26(mod 31)。
定理:设
p
p
p是素数,正整数
a
a
a是其自身模
p
p
p的逆,当且仅当
a
≡
1
(
m
o
d
p
)
a\equiv 1 (mod\ p)
a≡1(mod p)或
a
≡
−
1
(
m
o
d
p
)
a\equiv -1 (mod\ p)
a≡−1(mod p)。
证明:若
a
≡
1
(
m
o
d
p
)
a\equiv 1 (mod\ p)
a≡1(mod p)或
a
≡
−
1
(
m
o
d
p
)
a\equiv -1 (mod\ p)
a≡−1(mod p),有
a
2
≡
1
(
m
o
d
p
)
a^2\equiv 1 (mod\ p)
a2≡1(mod p),所以
a
a
a是其自身模
p
p
p的逆。反过来也成立。
3.4. 逆与除法取模
逆的一个重要应用是求除法的模。例如在catalan数中,有这样一个需求:求
(
a
/
b
)
m
o
d
m
(a/b) mod\ m
(a/b)mod m,即
a
a
a除以
b
b
b,然后对
m
m
m取模。由于这里
a
a
a和
b
b
b都是很大的数,做除法后再取模,会损失精度。用逆可以避免除法计算,设b的逆元是
b
−
1
b^{-1}
b−1,有:
(
a
/
b
)
m
o
d
m
=
(
(
a
/
b
)
m
o
d
m
)
∗
(
(
b
b
−
1
)
m
o
d
m
)
=
(
a
/
b
∗
b
b
−
1
)
m
o
d
m
=
(
a
b
−
1
)
m
o
d
m
(a/b) mod\ m = ((a/b) mod\ m)*((bb^{-1}) mod\ m) = (a/b*bb^{-1}) mod\ m = (ab^{-1}) mod\ m
(a/b)mod m=((a/b)mod m)∗((bb−1)mod m)=(a/b∗bb−1)mod m=(ab−1)mod m
经过上述推导,除法的模运算转换成了乘法模运算:
(
a
/
b
)
m
o
d
m
=
(
a
b
−
1
)
m
o
d
m
(a/b) mod\ m = (ab^{-1}) mod\ m
(a/b)mod m=(ab−1)mod m。
下面是一个除法取模的例题。
Detachment http://acm.hdu.edu.cn/showproblem.php?pid=5976
题目描述:把一个整数X分成多个整数的和:x = a1 + a2 + …,且ai ≠ aj,使得s = a1 ∗a2 * …最大。
输入:第一行是T,表示测试用例数量,后面有T行,每一行一个整数表示X。1 ≤ T ≤ 10^6, 1 ≤ X ≤ 10^9。
输出:对每个用例,首先计算最大的s,然后输出它对mod = 10^9+7的取模。
题解:如何分解X,才能使积s最大?这是小学奥数题,读者可以自己举例子推理。
首先,分解的数越多,积s越大。比如X分解为2个数,对比不分解的1个数X:(X - k)(X + k) > X。
其次,分解的数越接近,积越大。例如分成2个数,对比X/2 - 1、X/2 +1和X/2 - k、X/2 +k,有:(X/2 - 1)(X/2 +1) > (X/2 - k)(X/2 + k)。
结论是:把X尽量分为更多连续数的和,得到的积s最大。为了分解更多,就从2开始分解:X = 2 + 3 + 4 + 5 + …。从1开始分解不好,因为1对乘积没有贡献。如果还有余数,就把余数拆开,加到其他数上:从后往前加,每个数加上1,这样可以保证每个数都不同。例如17 = 2 + 3 + 4 + 5 + 3,最后有个余数3,把它拆成3个1,加到后面3个数上,得:17 = 2 + 4 + 5 + 6。再例如13 = 2 + 3 + 4 + 4,后面的余数4,拆成4个1,但是前面只有3个数,不够用,多的1再加在最后,得:13 = 3 + 4 + 6。
分解有两种情况:
(1)
2
×
3
×
4
×
.
.
.
×
(
i
−
1
)
×
(
i
+
1
)
×
.
.
.
×
k
×
(
k
+
1
)
2×3×4×...×(i-1)×(i+1)×...×k×(k+1)
2×3×4×...×(i−1)×(i+1)×...×k×(k+1),中间少个
i
i
i;
(2)
3
×
4
×
.
.
.
×
i
×
(
i
+
1
)
×
.
.
.
×
k
×
(
k
+
2
)
3×4×...×i×(i+1)×...×k×(k+2)
3×4×...×i×(i+1)×...×k×(k+2),前面少个2,后面多个
k
+
2
k+2
k+2;
求s的时候,先计算连续的乘积
a
a
a,然后除以
i
i
i,或者除以2乘以k+2。例如第(1)种情况,
s
=
a
/
i
s = a/i
s=a/i,输出的结果是
(
a
/
i
)
%
m
o
d
(a/i)\ \%\ mod
(a/i) % mod。除法取模需要用到逆。本题的模10^9 + 7正好是个素数,所以求逆用扩展欧几里得或费马小定理都行。
下面给出编码,细节有:
(1)先预计算出从2开始的前缀和和连续积,用于判断分解到哪个数为止。并用upper_bound()查找x的位置。
(2)把余数加到后面的数上去,并查找缺少的
i
i
i。
(3)计算结果。用逆计算除法取模。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxnum = 1e5; //分解的数不会超过50000个,请自己分析
const int mod = 1e9 + 7;
ll sum[maxnum], mul[maxnum]; //前缀和、连续积
ll fast_pow(ll x,ll y,int m){ //快速幂取模:x^y mod m
ll res = 1;
while(y) {
if(y&1) res*=x, res%=m;
x = (x*x) % m;
y>>=1;
}
return res;
}
long long mod_inverse(long long a,long long mod){ //费马小定理求逆,或者用扩展欧几里得求逆
return fast_pow(a,mod - 2,mod);
}
void init(){ //预计算前缀和、连续积
sum[1] = 0; mul[1] = 1;
for(int i=2; i<=maxnum; i++){
sum[i] = sum[i-1] + i; //计算前缀和
mul[i] = (i*mul[i-1]) % mod;//计算连续积
}
}
int main(){
init();
int T; scanf("%d",&T);
while(T--){
int x; scanf("%d",&x);
if( x == 1) {puts("1"); continue;} //特殊情况
int k = upper_bound(sum+1,sum+1+maxnum,x)-sum-1; //分解成k个数
int m = x - sum[k]; //余数
ll ans;
if(k==m)
ans = mul[k] * mod_inverse(2,mod) %mod * (k+2) % mod; //第2种情况
else
ans = mul[k+1] * mod_inverse(k-m+1,mod) % mod % mod; //第1种情况
printf("%lld\n",ans);
}
return 0;
}
4. 同余方程组
根据上一节的讨论,同余方程
a
x
≡
b
(
m
o
d
m
)
ax\equiv b (mod\ m)
ax≡b(mod m)有解时,即
g
c
d
(
a
,
m
)
gcd(a, m)
gcd(a,m) 能整除
b
b
b时,可以解得
x
≡
a
′
(
m
o
d
m
′
)
x\equiv a' (mod\ m')
x≡a′(mod m′),所以这也是同余方程的一般形式。本节讨论同余方程组的求解:
x
≡
a
1
(
m
o
d
m
1
)
x\equiv a_1 (mod\ m_1)
x≡a1(mod m1)
x
≡
a
2
(
m
o
d
m
2
)
x\equiv a_2 (mod\ m_2)
x≡a2(mod m2)
…
x
≡
a
r
(
m
o
d
m
r
)
x\equiv a_r (mod\ m_r)
x≡ar(mod mr)
例:有一个数
x
x
x,被3除余2,被5除余3,被7除余2,列成同余方程就是:
x
≡
2
(
m
o
d
3
)
x\equiv 2 (mod\ 3)
x≡2(mod 3)
x
≡
3
(
m
o
d
5
)
x\equiv 3 (mod\ 5)
x≡3(mod 5)
x
≡
2
(
m
o
d
7
)
x\equiv 2 (mod\ 7)
x≡2(mod 7)
求解结果是:
x
=
23
+
3
×
5
×
7
×
n
x = 23 + 3 × 5 ×7 × n
x=23+3×5×7×n,
n
≥
0
n ≥ 0
n≥0,或者写为
x
≡
23
(
m
o
d
3
×
5
×
7
)
x\equiv 23 (mod\ 3 × 5 × 7)
x≡23(mod 3×5×7),
x
x
x的最小正整数解是23。
本节介绍中国剩余定理和迭代法,前者是用于
m
1
,
m
2
,
.
.
.
,
m
r
m_1,m_2,...,m_r
m1,m2,...,mr两两互素情况下的优秀解法,后者是更一般条件下的通用解法。适用中国剩余定理的方程组肯定有解,而迭代法处理的更一般情况,可能是无解的。
4.1. 中国剩余定理
中国剩余定理1:设
m
1
,
m
2
,
.
.
.
,
m
r
m_1,m_2,...,m_r
m1,m2,...,mr是两两互素的正整数,则同余方程组
x
≡
a
1
(
m
o
d
m
1
)
x\equiv a_1 (mod\ m_1)
x≡a1(mod m1)
x
≡
a
2
(
m
o
d
m
2
)
x\equiv a_2 (mod\ m_2)
x≡a2(mod m2)
…
x
≡
a
r
(
m
o
d
m
r
)
x\equiv a_r (mod\ m_r)
x≡ar(mod mr)
有整数解,并且模
M
=
m
1
m
2
.
.
.
m
r
M = m_1m_2...m_r
M=m1m2...mr唯一,解为:
x
=
(
a
1
M
1
M
1
−
1
+
a
2
M
2
M
2
−
1
+
.
.
.
+
a
r
M
r
M
r
−
1
)
(
m
o
d
M
)
x=(a_1M_1M_1^{-1} + a_2M_2M_2^{-1} + ... + a_rM_rM_r^{-1}) (mod\ M)
x=(a1M1M1−1+a2M2M2−1+...+arMrMr−1)(mod M)
其中
M
i
=
M
/
m
i
M_i = M/m_i
Mi=M/mi,
M
i
−
1
M_i^{-1}
Mi−1为
M
i
M_i
Mi模
m
i
m_i
mi的逆元。
读者可以尝试自己证明。
例题:解同余方程组
x
≡
2
(
m
o
d
3
)
,
x
≡
3
(
m
o
d
5
)
,
x
≡
2
(
m
o
d
7
)
x\equiv 2 (mod\ 3),x\equiv 3 (mod\ 5),x\equiv 2 (mod\ 7)
x≡2(mod 3),x≡3(mod 5),x≡2(mod 7)。
解题步骤:
(1)
M
=
3
×
5
×
7
=
105
,
M
1
=
105
/
3
=
35
,
M
2
=
105
/
5
=
21
,
M
3
=
105
/
7
=
15
M = 3 ×5 ×7 = 105,M1 = 105/3 = 35,M2 = 105/5 = 21,M3 = 105/7 = 15
M=3×5×7=105,M1=105/3=35,M2=105/5=21,M3=105/7=15。
(2)求逆:
M
1
−
1
=
2
,
M
2
−
1
=
1
,
M
3
−
1
=
1
M_1^{-1} = 2,M_2^{-1} = 1,M_3^{-1} = 1
M1−1=2,M2−1=1,M3−1=1。
(3)最后计算
x
:
x
≡
2
×
35
×
2
+
3
×
21
×
1
+
2
×
15
×
1
≡
233
≡
23
(
m
o
d
105
)
x:x\equiv 2×35×2 + 3×21×1 + 2×15×1\equiv 233\equiv 23(mod 105)
x:x≡2×35×2+3×21×1+2×15×1≡233≡23(mod105)。
4.2. 迭代法
中国剩余定理的编码很容易,但是它的限制条件是方程组的
m
1
,
m
2
,
.
.
.
,
m
r
m_1,m_2,...,m_r
m1,m2,...,mr两两互素。如果不互素,该如何解题呢?这就是迭代法。
迭代法的思路很简单,就是每次合并两个同余式,逐步合并,直到合并完所有等式,只剩下一个,就得到了答案。
合并的时候,把同余方程转化为等式更容易操作。这是根据同余的一个性质:若
x
x
x和
a
a
a是整数,则
x
≡
a
(
m
o
d
m
)
x\equiv a (mod\ m)
x≡a(mod m)当且仅当存在整数,使得
x
=
a
+
k
m
x = a + km
x=a+km。
1、 示例
以方程组
x
≡
2
(
m
o
d
3
)
,
x
≡
3
(
m
o
d
5
)
,
x
≡
2
(
m
o
d
7
)
x\equiv 2 (mod\ 3),x\equiv 3 (mod\ 5),x\equiv 2 (mod\ 7)
x≡2(mod 3),x≡3(mod 5),x≡2(mod 7)为例,说明合并过程。下面的计算步骤,前3步合并了第1和第2个同余式,后面3步继续合并第3个同余式。
1)把第1个同余式
x
≡
2
(
m
o
d
3
)
x\equiv 2 (mod\ 3)
x≡2(mod 3)转换为
x
=
2
+
3
t
x = 2 + 3t
x=2+3t,代入第2个同余式得
2
+
3
t
≡
3
(
m
o
d
5
)
2 + 3t\equiv 3 (mod\ 5)
2+3t≡3(mod 5)。
2)求解
2
+
3
t
≡
3
(
m
o
d
5
)
2 + 3t\equiv 3 (mod\ 5)
2+3t≡3(mod 5)。
首先变为
3
t
≡
(
3
−
2
)
(
m
o
d
5
)
3t \equiv (3 - 2) (mod\ 5)
3t≡(3−2)(mod 5),即
3
t
≡
1
(
m
o
d
5
)
3t \equiv 1 (mod\ 5)
3t≡1(mod 5),因为
g
c
d
(
3
,
5
)
gcd(3, 5)
gcd(3,5)能整除1,所以有解。
然后求解
3
t
≡
1
(
m
o
d
5
)
3t \equiv 1 (mod\ 5)
3t≡1(mod 5):先求3模5的逆,是2,所以解得
t
≡
2
(
m
o
d
5
)
t\equiv 2(mod\ 5)
t≡2(mod 5),转换为等式
t
=
2
+
5
u
t = 2 + 5u
t=2+5u。
3)第1个和第2个同余式合并的结果。把
t
=
2
+
5
u
t = 2 + 5u
t=2+5u代入
x
=
2
+
3
t
x = 2 + 3t
x=2+3t得
x
=
8
+
15
u
x = 8 + 15u
x=8+15u,即
x
≡
8
(
m
o
d
15
)
x\equiv 8 (mod\ 15)
x≡8(mod 15)。
4)把
x
=
8
+
15
u
x = 8 + 15u
x=8+15u代入第3个同余式得:
8
+
15
u
≡
2
(
m
o
d
7
)
8 + 15u \equiv 2 (mod\ 7)
8+15u≡2(mod 7)。
5)求解
8
+
15
u
≡
2
(
m
o
d
7
)
8 + 15u\equiv 2 (mod\ 7)
8+15u≡2(mod 7)。
首先变为
15
u
≡
−
6
(
m
o
d
7
)
15u\equiv -6 (mod\ 7)
15u≡−6(mod 7),
g
c
d
(
15
,
7
)
gcd(15, 7)
gcd(15,7)能整除
−
6
-6
−6,有解。
然后求
15
u
≡
−
6
(
m
o
d
7
)
15u\equiv -6 (mod\ 7)
15u≡−6(mod 7):先求15模7的逆,是1,解得
u
≡
1
(
m
o
d
7
)
u\equiv 1(mod\ 7)
u≡1(mod 7),转换为
u
=
1
+
7
v
u = 1 + 7v
u=1+7v。
6)得到合并结果。把
u
=
1
+
7
v
u = 1 + 7v
u=1+7v代入
x
=
8
+
15
u
x = 8 + 15u
x=8+15u,得
x
=
23
+
105
v
x = 23 + 105v
x=23+105v,即
x
≡
23
(
m
o
d
105
)
x\equiv 23(mod\ 105)
x≡23(mod 105)。结束。
2、 编码步骤
下面改用丢番图方程的形式,总结合并两个同余式的编码方法,并以合并上面的前2个等式为例。
步骤 | 例子 |
---|---|
合并两个等式: x = a 1 + X m 1 x = a_1 + Xm_1 x=a1+Xm1 x = a 2 + Y m 2 x = a_2 + Ym_2 x=a2+Ym2 |
x
=
2
+
3
X
x = 2 + 3X
x=2+3X x = 3 + 5 Y x = 3 + 5Y x=3+5Y |
两个等式相等:
a
1
+
X
m
1
=
a
2
+
Y
m
2
a_1 + Xm_1 =a_2 + Ym_2
a1+Xm1=a2+Ym2 移项得: X m 1 + ( − Y ) m 2 = a 2 − a 1 Xm_1 + (-Y)m_2 = a_2 - a_1 Xm1+(−Y)m2=a2−a1 |
2
+
3
X
=
3
+
5
Y
2 + 3X = 3 + 5Y
2+3X=3+5Y 3 X + 5 ( − Y ) = 1 3X + 5(-Y) = 1 3X+5(−Y)=1 |
这是形如
a
X
+
b
Y
=
c
aX+bY = c
aX+bY=c的丢番图方程。 下面求解它。先用扩展欧几里得求 X 0 X_0 X0 | 得: X 0 = 2 X_0 =2 X0=2 |
X
X
X的通解是
X
=
X
0
c
/
d
+
(
b
/
d
)
n
X = X_0 c / d + (b/d)n
X=X0c/d+(b/d)n 最小值是 t = ( X 0 c / d ) m o d ( b / d ) t = (X_0 c / d) mod\ (b/d) t=(X0c/d)mod (b/d) |
t
=
(
X
0
c
/
d
)
m
o
d
(
b
/
d
)
t = (X_0 c / d) mod\ (b/d)
t=(X0c/d)mod (b/d) = ( 2 × 1 / 1 ) m o d ( 5 / 1 ) = 2 = (2×1/1) mod\ (5/1) = 2 =(2×1/1)mod (5/1)=2 |
把
X
=
t
X = t
X=t代入
x
=
a
1
+
X
m
1
x = a_1 + Xm_1
x=a1+Xm1 求得原等式的一个特解 x ′ x' x′ | 得 x ′ = 2 + 2 × 3 = 8 x' = 2 + 2×3 = 8 x′=2+2×3=8 |
合并后的新
x
=
a
+
X
m
x = a + Xm
x=a+Xm: m = m 1 m 2 / g c d ( m 1 , m 2 ) m = m_1m_2/gcd(m_1, m_2) m=m1m2/gcd(m1,m2) a = x ′ a = x' a=x′ |
m
=
3
×
5
/
1
=
15
m = 3×5/1 = 15
m=3×5/1=15 a = 8 a = 8 a=8 合并后的新方程是 x = 8 + 15 X x = 8 + 15X x=8+15X 即 x ≡ 8 ( m o d 15 ) x\equiv 8 (mod 15) x≡8(mod15) |
3、 例程
下面用一个模板题给出线性同余方程组的代码。
扩展中国剩余定理 洛谷P4777
题目描述:给定n组非负整数
a
i
,
m
i
a_i,m_i
ai,mi,求解关于
x
x
x的方程组的最小非负整数解。1≤n≤10^5,1 ≤ ai ≤ 10^12,
1
≤
m
i
<
a
i
1 ≤ m_i < a_i
1≤mi<ai,保证所有
a
i
a_i
ai的最小公倍数不超过 10^18。
x
≡
a
1
(
m
o
d
m
1
)
x\equiv a_1 (mod\ m_1)
x≡a1(mod m1)
x
≡
a
2
(
m
o
d
m
2
)
x\equiv a_2 (mod\ m_2)
x≡a2(mod m2)
…
x
≡
a
n
(
m
o
d
m
n
)
x\equiv a_n (mod\ m_n)
x≡an(mod mn)
输入:第一行是整数n,后面n行,每行两个非负整数
a
i
,
m
i
a_i,m_i
ai,mi。
输出:输出一行,为满足条件的非负整数x。
下面给出的代码2,和前面“编码步骤”基本一致。
注意代码中的细节,例如求
t
=
(
X
0
c
/
d
)
m
o
d
(
b
/
d
)
t = (X_0 c / d) mod\ (b/d)
t=(X0c/d)mod (b/d)的代码是
m
u
l
(
x
,
c
/
d
,
b
/
d
)
mul(x, c/d, b/d)
mul(x,c/d,b/d),目的是避免越界。其他细节见注释。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n;
ll ai[maxn], mi[maxn];
ll mul(ll a,ll b,ll mod){ //乘法取模:a*b % mod
ll res=0;
while(b>0){
if(b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
ll extend_gcd(ll a,ll b,ll &x,ll &y){ //扩展欧几里得
if(b == 0){ x=1; y=0; return a;}
ll d = extend_gcd(b,a%b,y,x);
y -= a/b * x;
return d;
}
ll excrt(){ //求解同余方程组,返回最小正整数解
ll x,y;
ll m1 = mi[1], a1 = ai[1]; //第1个等式
ll ans = 0;
for(int i=2;i<=n;i++){ //合并每2个等式
ll a2 = ai[i], m2 = mi[i]; // 第2个等式
//合并为:aX + bY = c
ll a = m1, b = m2, c = (a2 - a1%m2 + m2) % m2;
//下面求解 aX + bY = c
ll d = extend_gcd(a,b,x,y); //用扩展欧几里得求x0
if(c%d != 0) return -1; //无解
x = mul(x,c/d,b/d); //aX + bY = c 的特解t,最小值
ans = a1 + x* m1; //代回原第1个等式,求得特解x'
m1 = m2/d*m1; //先除再乘,避免越界。合并后的新m1
ans = (ans%m1 + m1) % m1; //最小正整数解
a1 = ans; //合并后的新a1
}
return ans;
}
int main(){
scanf("%d", &n);
for(int i=1;i<=n;++i)
scanf("%lld%lld",&mi[i],&ai[i]);
printf("%lld",excrt());
return 0;
}
公元3世纪《孙子算经》中有一个问题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?答曰:二十三。”1247年,秦九韶在《数学九章》中给出了求解的一般方法“大衍求一术”,被称为“中国剩余定理(Chinese Remainder Theorem)”。秦九韶是全能型的天才,在多个领域有建树。 ↩︎