中国剩余定理推导再理解

中国剩余定理:    

        m1, m2, m3, ... , mk 两两互质:(限制条件)
            x = a1 (mod m1) 
            x = a2 (mod m2)
            x = a3 (mod m3)
            ...
            x = ak (mod mk)
        若令 M = m1 * m2 * m3 * ... * mk
        Mi = M / mi,Mi(-1) 表示 Mi 模 mi 的逆.
        则有解 x = a1 * M1 * M1(-1) + a2 * M2 * M2(-1) + ... + ak * Mk * Mk(-1).

        其中Mi的逆元Mi(-1)可以用 Mi * x = 1 (mod mi) 来(扩展欧几里得解线性同余方程).
        
    
        证明过程如下:(以下证明过程,m 和 a 弄反了,余数是m1, m2, 模数是 a1, a2).
            x mod a1 = m1;
            x mod a2 = m2;
        即:
            x = k1 * a1 + m1;
            x = k2 * a2 + m2;
        则:
            k1 * a1 + m1 = k2 * a2 + m2;
        即:
            k1 * a1 - k2 * a2 = m2 - m1.


        有解等价于: gcd (a1, a2) | m2 - m1;
        exgcd求出k1, k2的值,则 x = k1 * a1 + m1 ;
        有不定方程所有解:
            k1 + k * (a2 / d), k2 + k * (a1 / d);
        所以 x 的所有解: x = (k1 + k * (a2 /d )) * a1 + m1
                                         x = a1 * k1 + m1 + k * (a1 * a2 / d)
                                         x = (a1 * k1 + m1) + k * [a1, a2].
                                         x = x0 + k * a.

代码实现:

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;  // 2e9 的数据好像一般都用long long.

LL exgcd (LL a, LL b, LL & x, LL & y) {
	if (!b) {
		x = 1;
		y = 0;
		return a;
	}
	LL d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

int main () {
	int n;
	cin >> n;

	LL a1, m1, a2, m2;
	cin >> m1 >> a1;
	LL x, k1, k2;

	bool flag = 0;
	for (int i = 2; i <= n; i ++ ) {
		
		cin >> m2 >> a2;
	
		int d = exgcd(m1, -m2, k1, k2); // m2 正负无所谓

		if ((a2 - a1) % d) {
			flag = 1;
			break;
		}

		k1 *= (a2 - a1) / d;  
		int t = m2 / d;

		k1 = (k1 % t + t) % t;   // 数据范围卡的很紧,所以k1取最小整数解.


		x = k1 * m1 + a1;
		a1 = x;
		m1 = abs(m1 / d * m2);   // m1正数、负数均可以.
	}

	if(flag) cout << "-1" << endl;
	else cout << (x % m1 + m1) % m1 << endl;  // 答案要求最小整数解.
	return 0;
}

THE END;

你可能感兴趣的:(数学)