原文
孙子定理是中国古代求解一次同余式组的方法,又称中国余数定理。一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,叫做“物不知数”问题,原文如下:
今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?
答曰:‘二十三’。
术曰:三三数之剩二,置一百四十;五五数之剩三,置六十三,七七数之剩二,置三十,并之。得二百三十三,以二百一十减之,即得。凡三三数之剩一,则置七十;五五数之剩一,则置二十一;七七数之剩一,则置十五;一百六以上以一百五减之即得。
翻译翻译就是:
一个整数除以3余2、除以5余3、除以7余2,求这个整数。
答案:23
解法:由于除以3余2,因此加上一个140;由于除以5余3,因此加上一个63;由于除以7余2,因此加上一个30;这三个数的和是140+63+30=233,再减去210,就得到了23了。
这么说吧,只要是除以3余了一个1,就加上一个70;只要是除以5余了一个1,就加上一个21;只要是除以7余了一个1,就加上一个15。然后累加。超过了106就减去105就行了。
古人的解法
具体解法分三步:(一个整数除以3余2、除以5余3、除以7余2,求这个整数)
- 找出三个数:从3和5的公倍数中找出除以7余1的最小数15,从3和7的公倍数中找出除以5余1 的最小数21,最后从5和7的公倍数中找出除以3余1的最小数70。
- 用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加(15x2+21x3+70x2)得到和233。
- 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
解法分析
计算一个整数 x x x ,使得它满足除以3余2、除以5余3、除以7余2。
如果能够找到三个整数 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 ,使得:
- x 1 x_1 x1除以3余2、除以5余0、除以7余0;
- x 2 x_2 x2除以3余0、除以5余3、除以7余0;
- x 3 x_3 x3除以3余0、除以5余0、除以7余2;
那么令
x
=
x
1
+
x
2
+
x
3
x=x_1+x_2+x_3
x=x1+x2+x3 ,就很容易验证这时的
x
x
x 就满足除以3余2、除以5余3、除以7余2。
继续细分
如果能够找到三个整数
y
1
,
y
2
,
y
3
y_1,y_2,y_3
y1,y2,y3 ,使得:
- y 1 y_1 y1除以3余1、除以5余0、除以7余0;
- y 2 y_2 y2除以3余0、除以5余1、除以7余0;
- y 3 y_3 y3除以3余0、除以5余0、除以7余1;
那么
x
=
x
1
+
x
2
+
x
3
=
2
y
1
+
3
y
2
+
2
y
3
x=x_1+x_2+x_3=2y_1 + 3y_2 + 2y_3
x=x1+x2+x3=2y1+3y2+2y3 ,就很容易验证这时的
x
x
x 就满足除以3余2、除以5余3、除以7余2。
若是求最小正整数
x
x
x 。那么只需将
x
x
x 除以3,5,7的最小公倍数,取余数就行。
最终问题: 如何解
y
1
,
y
2
,
y
3
y_1,y_2,y_3
y1,y2,y3 ,,,
以解
y
1
y_1
y1 为例,寻找整数
y
1
y_1
y1 使得
y
1
y_1
y1 除以3余1、除以5余0、除以7余0。
那么
y
1
y_1
y1一定是
5
×
7
=
35
5 \times 7=35
5×7=35 的倍数,假设
y
1
=
35
t
y_1=35t
y1=35t。
那么就有
35
t
≡
1
(
m
o
d
3
)
35t \equiv 1 \ (\mod 3)
35t≡1 (mod3),而这时的
t
t
t 就是35模3的逆,即
t
≡
3
5
−
1
(
m
o
d
3
)
t \equiv 35^{-1}(\mod 3)
t≡35−1(mod3) ,记作
[
3
5
−
1
]
3
\left[35^{-1} \right]_3
[35−1]3
那么
y
1
=
35
×
[
3
5
−
1
]
3
=
5
×
7
×
[
(
5
×
7
)
−
1
]
3
=
70
y_1=35 \times \left[35^{-1} \right]_3=5 \times 7 \times \left[(5 \times 7)^{-1} \right]_3=70
y1=35×[35−1]3=5×7×[(5×7)−1]3=70
同理:
y
2
=
3
×
7
×
[
(
3
×
7
)
−
1
]
5
=
21
y_2=3 \times 7 \times \left[(3 \times 7)^{-1} \right]_5=21
y2=3×7×[(3×7)−1]5=21
y
3
=
3
×
5
×
[
(
3
×
5
)
−
1
]
7
=
15
y_3=3 \times 5 \times \left[(3 \times 5)^{-1} \right]_7=15
y3=3×5×[(3×5)−1]7=15
那么便能解出
x
x
x,即
x
=
2
y
1
+
3
y
2
+
2
y
3
=
140
+
63
+
30
=
233
x =2y_1 + 3y_2 + 2y_3=140+63+30=233
x=2y1+3y2+2y3=140+63+30=233
最终结果就等于233除以105(3,5,7的最小公倍数)取余数,233%105=23,所以23就是最终结果。
补充个知识点。例:
23
23%3=2,23%5=3,23%7=2(除数 3 5 7 的最小公倍数是105)
23+105=128
128%3=2,128%5=3,128%7=2
23+210=233
233%3=2,233%5=3,233%7=2
显然,当一组除数和一组余数固定的时候,
有N个数除以这一组除数得到一样的一组余数,而这N个数是等差数列,公差是这一组除数的最小公倍数
例题
一个整数除以2余1,除以3余2、除以5余4、除以7余4,求这个整数。
解例题
一个整数除以2余1,除以3余2、除以5余4、除以7余4,求这个整数。
解:
解:
解:
第一步,找出四个数。
3
,
5
,
7
的最小公倍数是
105
,除以
2
余数是
1
,找到第一个数
105
2
,
5
,
7
的最小公倍数是
70
,除以
3
余数是
1
,找到第二个数
70
2
,
3
,
7
的最小公倍数是
42
,除以
5
余数是
2
,继续往上找,
84
,
126
,
168
等,发现
126
除以
5
余数是
1
,找到第三个数
126
2
,
3
,
5
的最小公倍数是
30
,除以
7
余数是
2
,继续往上找,
60
,
90
,
120
等,发现
120
除以
7
余数是
1
,找到第四个数
120
第二步,将这四个数分别乘以和对应的余数数值并求和。
y
=
1
×
105
+
2
×
70
+
4
×
126
+
4
×
120
=
1229
第三步,用
y
除以
2
,
3
,
5
,
7
的最小公倍数,余数就是最终结果。
1229
m
o
d
[
2
,
3
,
5
,
7
]
=
1229
m
o
d
210
=
179
,这个整数就是
179
第一步,找出四个数。 \\ 3,5,7的最小公倍数是105,除以2余数是1,找到第一个数105 \\ 2,5,7的最小公倍数是70,除以3余数是1,找到第二个数70 \\ 2,3,7的最小公倍数是42,除以5余数是2,继续往上找,84,126,168等,发现126除以5余数是1,找到第三个数126 \\ 2,3,5的最小公倍数是30,除以7余数是2,继续往上找,60,90,120等,发现120除以7余数是1,找到第四个数120 \\ 第二步,将这四个数分别乘以和对应的余数数值并求和。 \\ y = 1 \times 105 + 2 \times 70 + 4 \times 126 + 4 \times 120=1229 \\ 第三步,用y除以2,3,5,7的最小公倍数,余数就是最终结果。 \\ 1229 \mod \left[ 2,3,5,7 \right]=1229 \mod 210=179,这个整数就是179
第一步,找出四个数。3,5,7的最小公倍数是105,除以2余数是1,找到第一个数1052,5,7的最小公倍数是70,除以3余数是1,找到第二个数702,3,7的最小公倍数是42,除以5余数是2,继续往上找,84,126,168等,发现126除以5余数是1,找到第三个数1262,3,5的最小公倍数是30,除以7余数是2,继续往上找,60,90,120等,发现120除以7余数是1,找到第四个数120第二步,将这四个数分别乘以和对应的余数数值并求和。y=1×105+2×70+4×126+4×120=1229第三步,用y除以2,3,5,7的最小公倍数,余数就是最终结果。1229mod[2,3,5,7]=1229mod210=179,这个整数就是179
一元线性同余方程组
( 孙子算经中的方程 ) : { x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) ( 例题方程 ) : { x ≡ 1 ( m o d 2 ) x ≡ 2 ( m o d 3 ) x ≡ 4 ( m o d 5 ) x ≡ 4 ( m o d 7 ) \begin{aligned} (孙子算经中的方程): \left\{ \begin{array}{c} x \equiv 2 \ (\mod 3) \\ x \equiv 3 \ (\mod 5) \\ x \equiv 2 \ (\mod 7) \\ \end{array} \right. \qquad (例题方程): \left\{ \begin{array}{c} x \equiv 1 \ (\mod 2) \\ x \equiv 2 \ (\mod 3) \\ x \equiv 4 \ (\mod 5) \\ x \equiv 4 \ (\mod 7) \\ \end{array} \right. \end{aligned} (孙子算经中的方程):⎩ ⎨ ⎧x≡2 (mod3)x≡3 (mod5)x≡2 (mod7)(例题方程):⎩ ⎨ ⎧x≡1 (mod2)x≡2 (mod3)x≡4 (mod5)x≡4 (mod7)
无论是孙子算经中的问题还是给出的例题,都可以写成上述方程组。显然方程可以有多个解。
将中国剩余定理一般化,给出了以下的一元线性同余方程组:
(
S
)
:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
(S): \left\{ \begin{array}{c} x \equiv a_1 \ (\mod m_1) \\ x \equiv a_2 \ (\mod m_2) \\ \vdots \\ x \equiv a_n \ (\mod m_n) \\ \end{array} \right.
(S):⎩
⎨
⎧x≡a1 (modm1)x≡a2 (modm2)⋮x≡an (modmn)
中国剩余定理说明:假设整数m1,m2, … ,mn两两互质,则对任意的整数:a1,a2, … ,an,方程组S有解,并且通解可以用如下方式构造得到:
设
M
=
m
1
×
m
2
×
⋯
×
m
n
=
∏
i
=
n
10
m
i
M=m_1 \times m_2 \times \cdots \times m_n =\prod_{i=n}^{10}m_i
M=m1×m2×⋯×mn=∏i=n10mi 是整数m1,m2, … ,mn的乘积,并设
M
i
=
M
/
m
i
M_i=M/m_i
Mi=M/mi ,
M
i
M_i
Mi是除了mi以外的n-1个整数的乘积。
设
t
i
≡
M
i
−
1
(
m
o
d
m
i
)
t_i \equiv M^{-1}_i(\mod m_i)
ti≡Mi−1(modmi) 。(
t
i
t_i
ti为
M
i
M_i
Mi模
m
i
m_i
mi意义下的逆元,
M
i
t
i
≡
1
(
m
o
d
m
i
)
M_it_i \equiv 1(\mod m_i)
Miti≡1(modmi))
方程组S的通解形式为:
x
=
a
1
t
1
M
1
+
a
2
t
2
M
2
+
⋯
+
a
n
t
n
M
n
+
k
M
=
∑
i
=
1
n
a
i
t
i
M
i
+
k
M
,
k
属于整数
x=a_1t_1M_1+a_2t_2M_2+ \cdots +a_nt_nM_n+kM=\displaystyle \sum_{i=1}^{n}{a_it_iM_i}+kM,k属于整数
x=a1t1M1+a2t2M2+⋯+antnMn+kM=i=1∑naitiMi+kM,k属于整数
在模
M
M
M的意义下,方程组
(
S
)
(S)
(S)只有一个解:
x
=
(
∑
i
=
1
n
a
i
t
i
M
i
)
m
o
d
M
x=\bigg(\displaystyle \sum_{i=1}^{n}{a_it_iM_i}\bigg) \mod \ M
x=(i=1∑naitiMi)mod M
代码
用剩余定理的方式来解题
from functools import reduce
chu_num = [2, 3, 5, 7]
yu_num = [1, 2, 4, 4]
# # 三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2)
# chu_num = [3, 5, 7]
# yu_num = [2, 3, 2]
M = reduce(lambda x, y: x * y, chu_num) # 将数组chu_num里的所有数字做累乘运算
y = 0
for i in range(len(chu_num)):
inverse = M//chu_num[i] # 现在的 inverse 是最小公倍数
j = 1
base = M//chu_num[i]
while inverse % chu_num[i] != 1:
j += 1
inverse = base * j
#print(chu_num[i],yu_num[i] , j , base)
y += yu_num[i]*inverse
print(y)
print(y%M)
解同余方程组
from functools import reduce
import gmpy2
chu_num = [2, 3, 5, 7]
yu_num = [1, 2, 4, 4]
# # 三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2)
# chu_num = [3, 5, 7]
# yu_num = [2, 3, 2]
M = reduce(lambda x, y: x * y, chu_num) # 将数组chu_num里的所有数字做累乘运算
y = 0
for i in range(len(chu_num)):
Mi = M // chu_num[i]
# g == gcd(a,b) and g == a*s + b*t
g,s,t = gmpy2.gcdext(Mi,chu_num[i])
ti = s % chu_num[i]
#print(chu_num[i],yu_num[i] , ti , Mi)
y += yu_num[i] * ti * Mi
print(y)
print(y % M)
两代码就只有最后一部分不一样而已,但print打印的结果都是一样的,而且代码1中的inverse等于代码2中的ti * Mi
直接对比两代码的 y+= 部分:
代码1:y = 1 x 105 + 2 x 70 + 4 x126 + 4 x 120 = 1229
代码2:y = 1 x 1 x 105 + 2 x 1 x 70 + 4 x 3 x 42 + 4 x 4 x 40 = 1229
方程的余数部分是一样的,ti就是在中国剩余定理里,用最小公倍数找余数等于1时需要乘的倍数,Mi就是那个最小公倍数
补充
上文给的题目,除数都是两两互质的。所以在找它们的最小公倍数的时候,直接乘就行。但下面的例子就不能用同样的方法,它们只是相邻两个互质,但不是两两互质。
# 这个例子若用上面的代码
# 若用代码1的找法,第一轮的while循环直接死循环,3x4x5x6x7=2520,显然去找2520除以2余1的数,无论往上翻多少倍都不可能招到
# 代码2结果错误
chu_num = [2, 3, 4, 5, 6, 7]
yu_num = [1, 2, 1, 4, 5, 4]
上文给出的两种解法,不适用于不互质的情况,需要换个方式。
import math
chu_num = [2, 3, 4, 5, 6, 7]
yu_num = [1, 2, 1, 4, 5, 4]
# 上面这个是389,下面这个是179
# chu_num = [2, 3, 5, 7]
# yu_num = [1, 2, 4, 4]
def lcm(arr): # 求一组数的最小公倍数。例arr=[6,9,12],返回36
k=1
for i in arr:
k=math.lcm(k,i)
return k
num = 1
arr = [] # 这个数组是存储已经除过的数组(2-49之间的)
ind = 0
while ind < len(chu_num):
if num%chu_num[ind]==yu_num[ind]: # 判断余数是否相等
arr.append(chu_num[ind]) # 相等就将除数存入数组
ind += 1 # 下标+1,继续判断下一个
else:
num += lcm(arr) #如果余数不等的话,当前的数字=自身+数组的最小公倍数
print(num)