中国剩余定理SCL
【任务】
求出方程组
x
≡
a
i
(
m
o
d
m
i
)
(
0
⩽
i
<
n
)
x\equiv a_i(mod\space m_i)\space(0\leqslant i<n)
x≡ai(mod mi) (0⩽i<n)的解x。
其中
m
1
,
m
2
,
m
3
,
.
.
.
,
m
n
−
1
m_1,m_2,m_3,...,m_{n-1}
m1,m2,m3,...,mn−1两两互质。
【说明】
令
M
i
=
∏
j
=
i
m
j
M_i=\prod_{j\cancel{=}i}m_j
Mi=∏j=
imj。因为
(
M
i
,
m
i
)
(M_i,m_i)
(Mi,mi)=1,故存在
p
i
,
q
i
p_i,q_i
pi,qi,使
M
i
p
i
+
m
i
q
i
=
1
M_ip_i+m_iq_i=1
Mipi+miqi=1。
令
e
i
=
M
i
p
i
e_i=M_ip_i
ei=Mipi,有:
e
i
≡
{
0
(
m
o
d
m
j
)
,
j
=
i
1
(
m
o
d
m
j
)
,
j
=
i
\space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space e_i\equiv \begin{cases}0(mod\space m_j),j\cancel{=}i\\ 1(mod\space m_j),j=i \end{cases}
ei≡{0(mod mj),j=
i1(mod mj),j=i
故
e
0
a
0
+
e
1
a
1
+
e
2
a
2
+
.
.
.
+
e
n
−
1
a
n
−
1
e_0a_0+e_1a_1+e_2a_2+...+e_{n-1}a_{n-1}
e0a0+e1a1+e2a2+...+en−1an−1是方程的一个解。
在
[
0
,
∏
i
=
0
n
−
1
m
i
)
[0,\prod_{i=0}^{n-1}m_i)
[0,∏i=0n−1mi)中只有唯一解,将求出的解对
∏
i
=
0
n
−
1
m
i
\prod_{i=0}^{n-1}m_i
∏i=0n−1mi 取模即可。
【接口】
int CRT(int a[],int m[],int n);
复杂度:O(nlogm),其中m和每个
m
i
m_i
mi同阶。
输入:a,m——第i个方程表示为
x
≡
a
i
(
m
o
d
m
i
)
x\equiv a_i(mod\space m_i)
x≡ai(mod mi)
\space\space\space\space\space\space\space\space\space\space
n——方程个数
输出:方程组在
[
0
,
∏
i
=
0
n
−
1
m
i
)
[0,\prod_{i=0}^{n-1}m_i)
[0,∏i=0n−1mi)中的解
调用外部函数:拓展欧几里得
【代码】
int CRT( int a[], int m[], int n )//中国剩余定理
{
int M = 1;
for(int i=0;i<n;++i) M *= m[i];
int ret = 0;
for(int i=0;i<n;++i)
{
int x,y;
int tm = M/m[i];
extend_gcd(tm,m[i],x,y);//调用外部函数:拓展欧几里得
ret = (ret+tm*x*a[i])%M;
}
return ( (ret+M)%M )?( (ret+M)%M ):M;//特判:如果结果为0,需要输出几个模的最小公倍数
}
拓展中国剩余定理SCL
【任务】
求出方程组
x
≡
a
i
(
m
o
d
m
i
)
(
0
⩽
i
<
n
)
x\equiv a_i(mod\space m_i)\space(0\leqslant i<n)
x≡ai(mod mi) (0⩽i<n)的解x。
其中
m
1
,
m
2
,
m
3
,
.
.
.
,
m
n
−
1
m_1,m_2,m_3,...,m_{n-1}
m1,m2,m3,...,mn−1不一定两两互质。
【说明】
见本文下面详细说明。
【接口】
int extend_CRT(int a[],int m[],int n);
复杂度:O(nlogm),其中m和每个
m
i
m_i
mi同阶。
输入:a,m——第i个方程表示为
x
≡
a
i
(
m
o
d
m
i
)
x\equiv a_i(mod\space m_i)
x≡ai(mod mi)
\space\space\space\space\space\space\space\space\space\space
n——方程个数
输出:方程组在
[
0
,
∏
i
=
0
n
−
1
m
i
)
[0,\prod_{i=0}^{n-1}m_i)
[0,∏i=0n−1mi)中的解
调用外部函数:拓展欧几里得
【代码】
int extend_CRT( int a[], int m[], int n )//拓展中国剩余定理
{
int M = m[0];
int ret = a[0];
for(int i=1;i<n;++i)
{
int x,y;
int gcd = extend_gcd(M,m[i],x,y);//调用外部函数:拓展欧几里得
if( (a[i]-ret)%gcd != 0 ) return -1;//无解
int lcm = M/gcd*m[i];//先除后乘,防爆
int tm = ((a[i]-ret)/gcd)%(m[i]/gcd);
ret = (ret+M*x*tm)%lcm;
M = lcm;
}
return ((ret+M)%M)?((ret+M)%M):M;//特判:如果结果为0,需要输出几个模的最小公倍数
}
附:拓展欧几里得SCL
【接口】
int extend_gcd(int a,int b,int &x,int &y);
复杂度:O(logN),其中N和a,b同阶
输入:a,b——两个整数
\space\space\space\space\space\space\space\space\space\space
&x,&y——引用,ax+by=GCD(a,b)的一组解
输出:a和b的最大公约数
调用后x,y满足方程ax+by=GCD(a,b)
【代码】
int extend_gcd( int a, int b, int &x, int &y )//函数返回a,b的最大公约数
{
if( b==0 )
{
x = 1;
y = 0;
return a;
}
else
{
int r = extend_gcd(b,a%b,y,x);
y -= x*(a/b);
return r;
}
}
以下是中国剩余定理的学习笔记,是转载大神MashiroSky的中国剩余定理学习笔记。
中国剩余定理学习笔记
先看一道poj上的题目:【poj1006】 Biorhythms
题意:
人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这一天,人在对应的方面(体力,情感或智力)表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。
分析:
首先我们要知道,任意两个峰值之间一定相距整数倍的周期。假设一年的第N天达到峰值,则下次达到峰值的时间为N+Tk(T是周期,k是任意正整数)。所以,三个峰值同时出现的那一天(S)应满足
S
=
N
1
+
T
1
∗
k
1
=
N
2
+
T
2
∗
k
2
=
N
3
+
T
3
∗
k
3
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space S=N_1+T_1∗k_1=N_2+T_2∗k_2=N_3+T_3∗k_3
S=N1+T1∗k1=N2+T2∗k2=N3+T3∗k3
N
1
,
N
2
,
N
3
N_1,N_2,N_3
N1,N2,N3分别为为体力,情感,智力出现峰值的日期,
T
1
,
T
2
,
T
3
T_1,T_2,T_3
T1,T2,T3分别为体力,情感,智力周期。 我们需要求出
k
1
,
k
2
,
k
3
k_1,k_2,k_3
k1,k2,k3三个非负整数使上面的等式成立。
\space\space\space\space\space\space\space 想直接求出 k 1 , k 2 , k 3 k_1,k_2,k_3 k1,k2,k3貌似很难,但是我们的目的是求出S, 可以考虑从结果逆推。根据上面的等式,S满足三个要求:除以 T 1 T_1 T1余数为 N 1 N_1 N1,除以 T 2 T_2 T2余数为 N 2 N_2 N2,除以 T 3 T_3 T3余数为 N 3 N_3 N3。这样我们就把问题转化为求一个最小数,该数除以 T 1 T_1 T1余 N 1 N_1 N1,除以 T 2 T_2 T2余 N 2 N_2 N2,除以 T 3 T_3 T3余 N 3 N_3 N3。这就是著名的中国剩余定理,我们的老祖宗在几千年前已经对这个问题想出了一个精妙的解法。依据此解法的算法,时间复杂度可达到 O ( 1 ) O(1) O(1)。
中国剩余定理
\space\space\space\space\space\space\space 在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以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的余数),然后把三个乘积相加 15 ∗ 2 + 21 ∗ 3 + 70 ∗ 2 15∗2+21∗3+70∗2 15∗2+21∗3+70∗2得到和233。
- 用233除以3,5,7三个数的最小公倍数105,得到余数23,即 233 % 105 = 23 233\%105=23 233%105=23。这个余数23就是符合条件的最小数。
\space\space\space\space\space\space\space 就这么简单。我们在感叹神奇的同时不禁想知道古人是如何想到这个方法的,有什么基本的数学依据吗?
\space\space\space\space\space\space\space 我们将“孙子问题”拆分成几个简单的小问题,从零开始,试图揣测古人是如何推导出这个解法的。
\space\space\space\space\space\space\space 首先,我们假设 n 1 n_1 n1是满足除以3余2的一个数,比如2,5,8等等,也就是满足3∗k+2(k>=0)的一个任意数。同样,我们假设 n 2 n_2 n2是满足除以5余3的一个数, n 3 n_3 n3是满足除以7余2的一个数。
\space\space\space\space\space\space\space 有了前面的假设,我们先从 n 1 n_1 n1这个角度出发,已知 n 1 n_1 n1满足除以3余2,能不能使得 n 1 + n 2 n_1+n_2 n1+n2的和仍然满足除以3余2?进而使得 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和仍然满足除以3余2?
\space\space\space\space\space\space\space 这就牵涉到一个最基本数学定理,如果有 a % b = c a\%b=c a%b=c,则有 ( a + k ∗ b ) % b = c ( k 为 非 零 整 数 ) (a+k∗b)\%b=c(k为非零整数) (a+k∗b)%b=c(k为非零整数),换句话说,如果一个除法运算的余数为c,那么被除数与 k k k倍的除数相加(或相减)的和(差)再与除数相除,余数不变。这个是很好证明的。
\space\space\space\space\space\space\space 以此定理为依据,如果 n 2 n_2 n2是3的倍数, n 1 + n 2 n_1+n_2 n1+n2就依然满足除以3余2。同理,如果 n 3 n_3 n3也是3的倍数,那么 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和就满足除以3余2。这是从n1的角度考虑的,再从 n 2 , n 3 n_2,n_3 n2,n3的角度出发,我们可推导出以下三点:
- 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以3余2, n 2 n_2 n2和 n 3 n_3 n3必须是3的倍数。
- 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以5余3, n 1 n_1 n1和 n 3 n_3 n3必须是5的倍数。
- 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以7余2, n 1 n_1 n1和 n 2 n_2 n2必须是7的倍数。
\space\space\space\space\space\space\space 因此,为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和作为“孙子问题”的一个最终解,需满足:
- n 1 n_1 n1除以3余2,且是5和7的公倍数。
- n 2 n_2 n2除以5余3,且是3和7的公倍数。
- n 3 n_3 n3除以7余2,且是3和5的公倍数。
\space\space\space\space\space\space\space 所以,孙子问题解法的本质是从5和7的公倍数中找一个除以3余2的数 n 1 n_1 n1,从3和7的公倍数中找一个除以5余3的数 n 2 n_2 n2,从3和5的公倍数中找一个除以7余2的数 n 3 n_3 n3,再将三个数相加得到解。在求 n 1 , n 2 , n 3 n_1,n_2,n_3 n1,n2,n3时又用了一个小技巧,以 n 1 n_1 n1为例,并非从5和7的公倍数中直接找一个除以3余2的数,而是先找一个除以3余1的数,再乘以2。也就是先求出5和7的公倍数模3下的逆元,再用逆元去乘余数。
\space\space\space\space\space\space\space 这里又有一个数学公式,如果 a % b = c a\%b=c a%b=c,那么 ( a ∗ k ) % b = a % b + a % b + … + a % b = c + c + … + c = k ∗ c ( k > 0 ) (a∗k)\%b=a\%b+a\%b+…+a\%b=c+c+…+c=k∗c(k>0) (a∗k)%b=a%b+a%b+…+a%b=c+c+…+c=k∗c(k>0),也就是说,如果一个除法的余数为 c c c,那么被除数的 k k k倍与除数相除的余数为 k ∗ c k∗c k∗c。展开式中已证明。
\space\space\space\space\space\space\space 最后,我们还要清楚一点, n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3只是问题的一个解,并不是最小的解。如何得到最小解?我们只需要从中最大限度的减掉掉3,5,7的公倍数105即可。道理就是前面讲过的定理“如果 a % b = c a\%b=c a%b=c,则有 ( a − k ∗ b ) % b = c (a−k∗b)\%b=c (a−k∗b)%b=c”。所以 ( n 1 + n 2 + n 3 ) % 105 (n_1+n_2+n_3)\%105 (n1+n2+n3)%105 就是最终的最小解。
\space\space\space\space\space\space\space 这样一来就得到了中国剩余定理的公式:
设正整数
m
1
,
m
2
,
.
.
.
,
m
k
m_1,m_2,...,m_k
m1,m2,...,mk两两互素,则同余方程组
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
x
≡
a
3
(
m
o
d
m
3
)
.
.
.
x
≡
a
k
(
m
o
d
m
k
)
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space \begin{cases} x\equiv a_1(mod\space m_1)\\ x\equiv a_2(mod\space m_2)\\ x\equiv a_3(mod\space m_3)\\ \space\space\space\space .\\ \space\space\space\space .\\ \space\space\space\space .\\ x\equiv a_k(mod\space m_k)\end{cases}
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧x≡a1(mod m1)x≡a2(mod m2)x≡a3(mod m3) . . .x≡ak(mod mk)
有整数解。并且在模
M
=
m
1
⋅
m
2
⋅
.
.
.
⋅
m
k
M=m_1\cdot m_2\cdot...\cdot m_k
M=m1⋅m2⋅...⋅mk下的解是唯一的,解为
x
≡
(
a
1
M
1
M
1
−
1
+
a
2
M
2
M
2
−
1
+
.
.
.
+
a
k
M
k
M
k
−
1
)
m
o
d
M
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x\equiv(a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+...+a_kM_kM_k^{-1})mod\space M
x≡(a1M1M1−1+a2M2M2−1+...+akMkMk−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 的逆元。
中国剩余定理扩展——求解模数不互质情况下的线性方程组:
\space\space\space\space\space\space\space 普通的中国剩余定理要求所有的 m i m_i mi互素,那么如果不互素呢,怎么求解同余方程组?
\space\space\space\space\space\space\space 这种情况就采用两两合并的思想,假设要合并如下两个方程:
x = a 1 + m 1 x 1 x = a 2 + m 2 x 2 \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x=a_1+m_1x_1\\\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x=a_2+m_2x_2 x=a1+m1x1 x=a2+m2x2
\space\space\space\space\space\space\space
那么得到:
a
1
+
m
1
x
1
=
a
2
+
m
2
x
2
⇒
m
1
x
1
+
m
2
x
2
=
a
2
−
a
1
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space a_1+m_1x_1=a_2+m_2x_2\Rightarrow m_1x_1+m_2x_2=a_2-a_1
a1+m1x1=a2+m2x2⇒m1x1+m2x2=a2−a1
\space\space\space\space\space\space\space
我们需要求出一个最小的x使它满足:
x
=
a
1
+
m
1
x
1
=
a
2
+
m
2
x
2
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space x=a_1+m_1x_1=a_2+m_2x_2
x=a1+m1x1=a2+m2x2
\space\space\space\space\space\space\space 那么 x 1 x_1 x1和 x 2 x_2 x2就要尽可能的小,于是我们用扩展欧几里得算法求出 x 1 x_1 x1的最小正整数解,将它代回 a 1 + m 1 x 1 a_1+m_1x_1 a1+m1x1,得到x的一个特解 x ′ x' x′ ,当然也是最小正整数解。
\space\space\space\space\space\space\space 所以 x 的通解一定是 x ′ x' x′ 加上 l c m ( m 1 , m 2 ) ∗ k lcm(m_1,m_2)∗k lcm(m1,m2)∗k,这样才能保证x模 m 1 m_1 m1和 m 2 m_2 m2的余数是 a 1 a_1 a1和 a 2 a_2 a2。由此,我们把这个 x ′ x' x′ 当做新的方程的余数,把 l c m ( m 1 , m 2 ) lcm(m_1,m_2) lcm(m1,m2)当做新的方程的模数。(这一段是关键)
\space\space\space\space\space\space\space
合并完成:
x
≡
x
′
(
m
o
d
l
c
m
(
m
1
,
m
2
)
)
\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space x\equiv x'\space(mod\space lcm(m_1,m_2)\space)
x≡x′ (mod lcm(m1,m2) )
总结
\space\space\space\space\space\space\space 经过分析发现,中国剩余定理的孙子解法并没有什么高深的技巧,就是以下两个基本数学定理的灵活运用:
- 如果 a%b=c , 则有 (a+kb)%b=c (k为非零整数)。
- 如果 a%b=c,那么 (a*k)%b=kc (k为大于零的整数)。