中国剩余定理(CRT):求解模线性方程组

定义

 设有同余式组 (S):

(S)xa1(modm1)xa2(modm2)xa3(modm3)xak(modmk)

xai(modmi),i=1,2,..,k

 其中 ai mi 已知,且当 ij 时, 有 (mi,mj)=1 , 求x对模 m1m2...mk 的解。

解的情况

 有唯一解, 证明略。
 下面会构造出此解,然后证明其正确性和唯一性。
 设

M=ki=1mi//m

Mi=Mmi//mim

//M1iMi(mi)MiM1i1(modmi)

 则(S)的解为:

x=tM+i=1kaiMiM1i


x=(i=1kaiMiM1i)(modM)

 下证其正确性:

我们考查任一在[1, k]内的第i项: aiMiM1i , 有

aiMiM1i{ai1(modmj),j=i0(modmj),ji

这里不难理解, 第二个为0是因为 mj|Mi
所以x满足如下条件:
i{1,2,..,k} , 有
x=aiMiM1i+ji(ajMjM1J)ai+ji0ai(modmi)

综上, 此解正确。

 下面证明其唯一性

假设 x1 x2 都是方程组(S)的解,那么:
i{1,2,..,k} , 有 x1x20(modmi)
mi|(x1x2)
那么显然 M|(x1x2)
x1x2 之间相差M的整数倍,那么在模M的范围内,只有一解。

更通用的解法

 前面说的模线性方程组是k个m互素的情况, 那么更一般的呢?
 更一般的模线性方程也能求解,只不过需要从前往后遍历每一个式子, 每一个都与下一个式子合并,作为一个新的式子继续与下一个合并, 最后得出结果。

 重点!!!
 我们先假设k=2时的情况,然后再慢慢推广。
 此时(S)为

(S){xa1(modm1)(1)xa2(modm2)(2)

{x=k1m1+a1(3)x=k2m2+a2(4)

 我们将(3)(4)联立,得

k1m1=k2m2+a2a1
, 即
k1m1a2a1(modm2)(5)
//这个式子特别重要

 因为(5)式未知数只有 k1 ,所以我们用扩展gcd可以求出 k1 的值, 不妨设求出的 k1=t , 则对某t’, 有 k1=t+tm2 。我们将它代入(3)式,得到

x(a1+tm1)(modlcm(m1m2))(6)//lcm

 那么(6)式中的x即为同时满足(1)和(2)的x值,至此k=2的情况讨论完毕。
 至于k>2的情况, 我们可以继续将(6)式与(3)式像(1)式和(2)式那样执行上面的操作,这样到最后得到的x就满足所有的条件了。
 具体的操作可参考代码理解, 这里简单说一下中间量的维护:
 每次对2个式子进行以上操作时, 第二个式子和以前一样,不作讨论,而第一个式了如果要写成 xa1(modm1) 的形式的话, a1 m1 都是要变的,那么变成什么样子决定着上述算法的最终实现。

 首先来说 m1 的变化 , 从(1)到(6), m1 变成了lcm( m1,m2 ), 如果接着往下走的话, m1 又会变成lcm( m1,m2,m3 ),…, 直到最后, m1变成所有m的最小公倍数。
 而对于 a1 的变化 ,我们可以这样想,如果k=1的话, 结果ans是等于 a1 的,而k = 2的时候, 观察(6)我们会发现, a1 变成了 a1+tm1 , 那结果呢, 结果ans(不看模)也变成了 a1+tm1 , 如果再往下写你会发现, ans 不是变成了 a1+tm1 , 而是变成了 ans+tlcm , 其中t 是由我们解(5)得到的。
 这里说的有点乱,具体的细节可以结合下面的实现代码去理解。

实现

 m[i] 与m[j] 互质:

int CRT(int a[], int m[], int k) {
    int i, d, x, y, Mi, ans = 0, M = 1;
    for (i = 0; i < k; i++) M *= m[i]; 
    for (i = 0; i < k; i++) {
        Mi = M / m[i];
        d = extgcd(m[i], Mi, x, y);     // y 为逆元 -- Mi*y === 1 (% m[i])
        ans = (ans + a[i]*y*Mi) % M;
    }
    if (ans > 0) return ans;
    else return (ans + M);
}

 m[i] 与m[j] 不一定互质:

LL mod(LL a, LL m) {    //处理取模
    LL res = a%m;
    if(res <= 0) res += m;
    return res;
}
bool CRT(LL a[], LL m[], LL k, LL& ans) {
    ans = a[0];
    LL lcm = m[0], x, y, d;
    if(ans == 0) ans = m[0];
    for(LL i = 1; i < k; ++i) {
        d = extgcd(lcm, m[i], x, y);    //求t: t = (a[i]-ans)/d*x;
        if((a[i]-ans)%d) return 0;
        ans = mod(ans + lcm*mod((a[i]-ans)/d*x, m[i]/d),(lcm/d*m[i]));
        lcm = lcm/d*m[i];
    }
    return 1;
}

例题

POJ - 1006 Biorhythms
HDU - 1573X问题
POJ - 2891 Strange Way to Express Integers

平生写过的最浮夸的博客。。

你可能感兴趣的:(数论,知识点)