同余方程和中国剩余定理
今天做题的时候化简式子,化简到一半发现几乎就是中国剩余定理的板题,然后发现蒟蒻我不会写中国剩余定理,然后就来补习了23333333
解线性模方程
解法
\( ax \equiv b \mod q \) 可以转化为\( ax + kq = b \) 。
当\( gcd(a,q)|b \) 时,方程有解,否则无解。
当\( a \) 和\( q \) 不互质时,令方程两边同时处以\( gcd(a,q) \) ,得到:
\( \dfrac{a}{gcd(a,q)}x+k\dfrac{q}{gcd(a,q)}=\dfrac{b}{gcd(a,q)} \)
也就是:
\( \dfrac{ax}{gcd(a,q)} \equiv \dfrac{b}{gcd(a,q)} \mod \dfrac{q}{gcd(a,q)} \)
令\( a_1=\dfrac{a}{gcd(a,q)},b_1=\dfrac{b}{gcd(a,q)},q_1=\dfrac{q}{gcd(a,q)} \) ,于是有:
\( a_1x \equiv b_1 \mod q_1 \) ,其中\( a_1 \) 和\( q_1 \) 互质。此时,在方程两边乘\( a_1 \) 在模\( q_1 \) 意义下的逆元,就可以得到:
\( x\equiv b_1\cdot a_1^{-1} \mod q_1 \)
所以\( x=b_1\cdot a_1^{-1} + k\cdot q_1 \) ,其中\( k\in Z^+ \)
等价类
对于上式\( x=b_1\cdot a_1^{-1} + k\cdot q_1 \) ,对于任意\( i,j\in Z^+,i \geqslant j \) ,显然,我们有:
\( (b_1\cdot a_1^{-1}+i\cdot q_1)-(b_1\cdot a_1^{-1} + j\cdot q_1)= (i-j)q_1 \)
如果\( (b_1\cdot a_1^{-1}+i\cdot q_1) \) 和\( (b_1\cdot a_1^{-1} + j\cdot q_1) \) 同余,那么\( (i-j)q_1 \) 是\( q \) 的倍数。因此,\( (i-j) \) 必须是\( gcd(a,q) \) 的倍数。
换句话说,在模\( q \) 的剩余系下,\( ax \equiv b \mod q \) 恰好有\( gcd(a,q) \) 个解。令\( t = b_1\cdot a_1^{-1} \) ,则这\( gcd(a,q) \) 个解分别是:\( t, t+p_1 , t + 2p_1, \cdots , t + (gcd(a,q)-1)p_1 \) 。
这里稍作解释:为什么说,在模\( q \) 的剩余系下,只有这\( gcd(a,q) \) 个解呢?
考虑\( x_1=t \) 和\( x_2=t+gcd(a,q)p_1 \) ,由上面的定义可知:\( gcd(a,q)p_1=p \) 。所以,\( ax_1 \mod p \) 和\( ax_2 \mod p \) 在任何意义下都是完全一致的。同理可得其他\( x=t + kp_1 \) 的情况。所以,只保留这\( gcd(a,q) \) 个解就能涵盖所有的情况。
解同余方程组(中国剩余定理)
变量还是一个,但是变成了如下的方程组:
\begin{cases} x \equiv a_1 \mod m_1 \cr x\equiv a_2 \mod m_2 \cr \cdots \cr x\equiv a_n \mod m_n \end{cases}
假设此时所有的\( m_i \) 两两互质。令\( M=\prod\limits_{i=1}^{n}m_i,w_i=\dfrac{M}{m_i} \) (\( w_i \) 就是除了\( m_i \) 以外其他\( m \) 的乘积),那么有\( gcd(w_i, m_i)=1 \) (\( w_i \) 和\( m_i \) 互质)。
所以,我们很容易求出\( w_i \) 在模\( m_i \) 意义下的逆元:
\( t_iw_i + ym_i = 1 \) ,拓展欧几里得算法可以求出\( t_i \) 。
所以,显然有\( t_iw_i \equiv 1 \mod m_i \) (\( t_i \) 是\( w_i \) 的逆元)。
也就是:
\( t_iw_i = km_i + 1, k\in Z \) ,等价于
\( t_iw_i + km_i = 1, k \in Z \)
两边同时乘\( a_i \) ,得:
\( t_ia_iw_i + ka_im_i = a_i \)
我们知道,\( k \) 表示的是模\( m_i \) 的次数,所以我们可以把\( ka_i \) 看作\( k \) 。
于是我们得到:
\( t_ia_iw_i + km_i = a_i \)
又因为我们有:\( x \equiv a_i \mod m_i \) (看题干),也就是:
\( x = a_i + km_i \)
\( x + km_i = a_i \)
所以,我们得到如下方程组:
\( \begin{cases} t_ia_iw_i + km_i = a_i \cr x + km_i = a_i \end{cases} \)
注意这里两个\( k \) 不一定相等,但是因为我们要保证的是\( x \) 对\( m_i \) 取模的结果是\( a_i \) ,所以整数\( k \) 对结果并没有影响(因为它们的乘数是\( m_i \) )。所以我们得到:\( x=t_ia_iw_i \) 。
由定义可知,对于任意\( j \neq i \) ,恒有\( m_j | w_i \) 。
所以,由取模的性质,对于任意的整数\( j \) 有:
\( \begin{align} & \sum\limits_{i=1}^{n}t_ia_iw_i \mod m_j \cr = &\left( t_ja_jw_j + \sum\limits_{i=1}^{j-1}t_ia_iw_i + \sum\limits_{i=j+1}^{n}t_ia_iw_i\right) \mod m_j \cr = & t_ja_jw_j \mod m_j + \sum\limits_{i=1}^{j-1}t_ia_iw_i \mod w_j+ \sum\limits_{i=j+1}^{n}t_ia_iw_i \mod w_j \cr = &t_ja_jw_j \mod m_j \cr = & a_j\end{align} \)
(因为\( m_j | w_i \) ,所以\( \sum\limits_{i=1}^{j-1}t_ia_iw_i \mod w_j = 0 \) ,\( \sum\limits_{i=j+1}^{n}t_ia_iw_i \mod m_j = 0 \) )
所以,\( x \) 的一组解就是\( \sum\limits_{i=1}^{n}t_ia_iw_i \) ,其中\( t_i \) 是\( w_i \) 关于模\( m_i \) 的逆元。
在模\( M \) 的剩余系下,方程组就有唯一一组解:\( \sum\limits_{i=1}^{n}t_ia_iw_i \mod M \) 。
代码如下:
// 测试代码
#include <cstdio>
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll r = exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - a / b * y;
return r;
}
// n个方程:x = a[i] % m[i], (0 <= i < n)
ll CRT(int n, int *a, int *m) {
ll M = 1, t, y, x = 0;
for (int i = 0; i < n; i++) M *= m[i];
for (int i = 0; i < n; i++) {
ll w = M / m[i];
exgcd(w, m[i], t, y);
x = ((x + t * w * a[i]) % M + M) % M;
}
return x;
}
int a[1000], m[1000];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", a + i);
for (int i = 0; i < n; i++)
scanf("%d", m + i);
printf("%lld\n", CRT(n, a, m));
return 0;
}
参考资料:
中国剩余定理证明
刘汝佳《算法竞赛入门经典——训练指南》
本文迁移自作者原博客:icysky's Blog
本文作者:icysky
原文链接:同余方程和中国剩余定理
版权声明:本博客所有文章除特别声明外,均采用CC-BY-NC-SA 4.0许可协议。icysky's Blog 版权所有,转载请注明出处。