中国剩余定理(最详细说明 非互质情况)

中国剩余定理:

找了半天发现网上居然找不到完整的证明,只能自己默默花时间推了。。。

求满足 x ≡ 2 ( m o d    3 )    ,    x ≡ 3 ( m o d    5 )    ,    x ≡ 2 ( m o d    7 ) x\equiv 2(mod\;3)\;,\;x\equiv 3(mod\;5)\;,\;x\equiv 2(mod\;7) x2(mod3),x3(mod5),x2(mod7)的最小正整数解x
中国剩余定理(最详细说明 非互质情况)_第1张图片


可相加性证明:
   \\\;
140满足%3=2,而且是5和7的倍数,也就是说加上140对其他数的取余结果不会有影响
   \\\;
同理,其他的数加上这个数就可以在不影响自生取余的情况下附带满足%3=2的性质
   \\\;
所以把所有这些数加起来就是满足答案的一个解,而在这个时候对于这个得到的数尽可能的消小(减去所有数的公倍数不会改变性质),就是答案

数学表示为:
   \\\;
问 题 为 x % m = a , 第 i 个 式 子 的 m 为 m i , a 为 a i , M 为 所 有 m 的 乘 积 , M i 为 M / m i 问题为x\%m=a, 第i个式子的m为m_i , a为a_i , M为所有m的乘积 , M_i为M/m_i x%m=a,immi,aai,Mm,MiM/mi
列出:
k 1 ∗ 7 ∗ 5 ≡ 2 ( m o d    3 ) k_1*7*5\equiv 2(mod \;3) k1752(mod3)
k 2 ∗ 7 ∗ 3 ≡ 3 ( m o d    5 ) k_2*7*3\equiv 3(mod \;5) k2733(mod5)
k 3 ∗ 3 ∗ 5 ≡ 2 ( m o d    7 ) k_3*3*5\equiv 2(mod \;7) k3352(mod7)
左边的部分为上面的140,63,30

k怎么求呢?以第一个为例:
k 1 ∗ 7 ∗ 5 ≡ 2 ∗ ( 7 ∗ 5 ) ∗ i n v ( 7 ∗ 5 , 3 ) k_1*7*5\equiv2*(7*5)*inv(7*5,3) k1752(75)inv(75,3),使得右边为 7 7 7 5 5 5的倍数,但是我们这里不进行对 3 3 3的取模操作

这样既保证了右边部分可以 % m i = a i \% m_i = a_i %mi=ai,而且是其他m的倍数

因为这里的m都是互质的所以上文中提到的公倍数就是M

代码:

void exgcd(LL a,LL b,LL &x,LL &y) { //a在模b下的逆元为x
    if(b==0) {
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    LL tp=x;
    x=y;
    y=tp-a/b*y;
}
int main() {
    while(cin>>n) {
        M=1;
        ans=0;
        for(LL i=1; i<=n; i++) {
            m[i]=read(),a[i]=read(); // ans % m = a;
            M*=m[i];
        }
        for(LL i=1; i<=n; i++) {
            LL Mi=M/m[i];
            LL inv,y;
            exgcd(Mi,m[i],x,y);
            ans+=a[i]*Mi%M *inv%M;
            ans%=M;
        }
        cout<<ans<<'\n';
    }
}

扩展中国剩余定理(不互质情况下的做法):

上面这么做的前提是多个m互质,如果不互质,就不能这么做了

我们就需要用合并的方法,合并下面的两个方程:
x = a 1 + m 1 x 1 x = a 2 + m 2 x 2 x=a_1+m_1x_1\\x=a_2+m_2x_2 x=a1+m1x1x=a2+m2x2

x = a 1 + m 1 x 1 = a 2 + m 2 x 2 x=a_1+m_1x_1=a_2+m_2x_2 x=a1+m1x1=a2+m2x2
m 1 x 1 + m 2 ( − x 2 ) = a 2 − a 1 m_1x_1+m_2(-x_2)=a_2-a_1 m1x1+m2(x2)=a2a1
用扩展欧几里得求这个二元一次方程组,设 d = g c d ( m 1 , m 2 ) , c = a 2 − a 1 d=gcd(m_1,m_2),c=a_2-a_1 d=gcd(m1,m2),c=a2a1
求出x1的最小整数解 x 1 = ( x 1 ∗ c d % m 2 d + m 2 d ) % m 2 d x_1=(x_1*\dfrac{c}{d}\%\dfrac{m2}{d}+\dfrac{m2}{d})\%\dfrac{m2}{d} x1=(x1dc%dm2+dm2)%dm2,有 x = m 1 ( x 1 + k ∗ ( m 2 / d ) ) + a 1 x = ( m 1 ∗ m 2 / d ) ∗ k + ( m 1 ∗ x 1 + a 1 ) x=m_1(x_1+k*(m2/d))+a1\\x=(m_1*m_2/d)*k+(m_1*x_1+a_1) x=m1(x1+k(m2/d))+a1x=(m1m2/d)k+(m1x1+a1)

为什么是k*(m2/d)?
   \\\;
因为扩展欧几里得的x有多个解,所以可以加上这部分,在保证等式成立的前提下,使等式的右边出现一个变量使得保持结构的不变性,以迭代

便化成了一个新的式子,不断迭代就可以出结果

当然,k=0时便是最小整数解

模板(poj 2891)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;

#define LL long long
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int n;
void ex_gcd(LL a, LL b, LL &d, LL &x, LL &y) {
    if (!b) {
        d = a, x = 1, y = 0;
    } else {
        ex_gcd(b, a % b, d, y, x);
        y -= x * (a / b);
    }
}
LL ex_crt(LL *m, LL *r, int n) {
    LL M = m[1], R = r[1], x, y, d;
    for (int i = 2; i <= n; ++i) {
        ex_gcd(M, m[i], d, x, y);
        if ((r[i] - R) % d)
            return -1;
        x = (r[i] - R) / d * x % (m[i] / d);
        R += x * M;
        M = M / d * m[i];
        R %= M;
    }
    return R > 0 ? R : R + M;
}
LL m[maxn], r[maxn];
int main() {
    while (~scanf("%d",&n)) {
        for (int i = 1; i <= n; ++i)
            scanf("%lld%lld", &m[i], & r[i]);
        printf("%lld\n",ex_crt(m,r,n));
    }
    return 0;
}

你可能感兴趣的:(数论/数学,知识点,ACM中的数学问题合集)