孙子定理
作者:互联网
孙子定理
孙子定理,又称之为中国剩余定理(Chinese Remainder Theorem, CRT)可以求解如下形式的一元线性同余方程组(其中\(n_1,n_2,\dots,n_k\)两两互质)。
\[\begin{cases} x\equiv a_1(mod\:n_1)\\ x\equiv a_2(mod\:n_2)\\ \quad\quad \vdots \\ x\equiv a_k(mod\:n_k)\\ \end{cases} \]中国剩余定理说明:假设整数\(m_1,m_2,\dots,m_n\)两两互质,则对任意的整数:\(a_1,a_2,\dots,a_n\),方程组有解,并且我们可以用如下方式构造得到:
设\(M=m_1\times m_2\times\dots\times m_n=\prod_{i=1}^{n}m_i\)是整数\(m_1,m_2,\dots,m_n\)的乘积,并设\(M_i=\frac{M}{m_i},\forall i\in\{1,2,3,\dots,n\}\)是除了\(m_i\)以外的\(n-1\)个整数的乘积。设\(t_i\)是\(M_i\)模\(m_i\)下的逆元,则方程组的通解形式为:\(x=a_1t_1M_1+a_2t_2M_2+\dots+a_nt_nM_n+kM=kM+\sum_{i=1}^na_it_iM_i,k\in Z\)。
所以在模\(M\)的意义下,方程组的只有一个解:\(x=(\sum_{i=1}^na_it_iM_i)(mod\:M)\)。
算法流程:
- 计算所有模数的积\(n\);(有的上面说计算它们的最小公倍数,其实都一样它们两两互质)
- 对于第\(i\)个方程:
- 计算\(m_i=\frac{n}{n_i}\);
- 计算\(m_i\)在模\(n_i\)意义下的逆元\(m_i^{-1}\);
- 计算\(C_i=m_i\times m_i^{-1}\)(不要对\(n_i\)取模);
- 方程组的唯一解为:\(a=\sum_{i=1}^ka_i\times c_i(mod\:n)\)。
void exgcd(ll a, ll b, ll &x, ll &y){//扩展欧几里得
if(b == 0){
x = 1;
y = 0;
return ;
}
exgcd(b,a%b,x,y);
ll temp = x;
x = y;
y = temp - a / b * y;
return ;
}
ll qmul(ll n, ll b, ll mod){
ll res = 0;
b = b % mod;
while(n > 0){
if(n & 1) res = (res + b) % mod;
b = (b + b) % mod;
n >>= 1;
}
return res % mod;
}
ll CRT(){
ll M = 1, res = 0;
for(int i = 1; i <= n; i++){
M *= m[i];
}
for(int i = 1; i <= n; i++){
ll x, y;
ll Mi = M / m[i];
exgcd(Mi,m[i],x,y);
x = (x % m[i] + m[i]) % m[i];//防止x为负数
res = (res + qmul(qmul(Mi,x,M),a[i],M)) % M;//使用快速幂不然有的会爆long long
}
if(res < 0) res += M;
return res;
}
扩展——求解模数不互质情况下的线性方程组
\[\begin{cases} x\equiv a_1(mod\:m_1)\:\:\:(1)\\ x\equiv a_2(mod\:m_2)\:\:\:(2)\\ \end{cases} \]将\(1,2\)两式合并我们可以得到:\(m_1\times x_1+a_1=m_2\times x_2+a_2\),即\(m_1\times x_1+(-m_2)\times x_2=a_2-a_1\)。运用扩展欧几里得算法,我们可以算出\(x_1,x_2\)进而我们可以知道\(x_1,x_2\)的解集\(x_1=x_1+k\times\frac{m_2}{d},x_2=x_2+k\times\frac{m_1}{d}\)(\(k\)为任意常数)。其中\(gcd(m_1,m_2)=d\),并且我们知道\(gcd(m_1,m_2)|(a_2-a_1)\),否则无解。这里对于\(x_1,x_2\)解集的证明可以自行证明。
接下来,将\(x_1\)的解集单独带入\((1)\)式中:\(x=m_1\times x_1+a_1+k\times\frac{m_1\times m_2}{d}\)。这里我们知道\(m_1,x_1,a_1,[m_1,m_2]\)的值是已经确认的,只有\(k\)的值是不定的,所以我们可以将式子看成\(x=x_0+k\times a\)(即\(x\equiv x_0(mod\:a)\))。所以我们不断重复\(n-1\)次,即可将\(n\)个式子合并,所以最后的结果为\(x_0\:mod\:a\)。
代码如下:
ll CHT(){
ll a1 = a[1], m1 = m[1];
for(ll i = 2; i <= n; i++){
ll a2 = a[i], m2 = m[i];
ll x, y;
ll gcd = exgcd(m1,m2,x,y);
ll c = a2 - a1;
if(c % gcd){
return 0;//无解
}
x = x * c / gcd;
ll temp = abs(m2/gcd);
x = (x % temp + temp) % temp;
a1 = x * m1 + a1;
m1 = abs(m1 * m2 / gcd);
}
return (a1 % m1 + m1) % m1;
}
标签:dots,res,定理,times,孙子,equiv,ll,mod 来源: https://www.cnblogs.com/hy2001/p/14823716.html