中国剩余定理

复习中国剩余定理(CRT)

起源

在《孙子算经》中的“物不知数”的问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。

问题转换为求解方程组:

\[\left\{\begin{array}{l} x \equiv 2 \bmod 3 \\ x \equiv 3 \bmod 5 \\ x \equiv 2 \bmod 7 \end{array}\right.\]

求解思路:

  • 先将余数写成和的形式:\(2+3+2\)
  • 为了满足第一个方程,即模3,消去后两项,给后两项乘3,得\(2+3*3+2*3\)
    • 为了满足第二个方程,即模5,消去后第一项和第三项,给乘5,得\(2*5+3*3+2*3*5\)
    • 为了满足第三个方程,即模7,消去后前两项,给乘7,得\(2*5*7+3*3*7+2*3*5\)
  • 将结果带入第一个方程,得到\(2*5*7\),为了消去\(5*7\),给乘\((5*7)^{-1}(mod 3)=1\),得\(2*5*7*(5*7)^{-1}(mod 3)+3*3*7+2*3*5\)
    • 将结果带入第二个方程,得到\(3*3*7\),为了消去\(3*7\),给乘\((3*7)^{-1}(mod 5)=4\),得\(2*5*7*(5*7)^{-1}(mod 3)+3*3*7*(3*7)^{-1}(mod 5)+2*3*5\)
    • 将结果带入第三个方程,得到\(2*3*5\),为了消去\(3*5\),给乘\((3*5)^{-1}(mod 7)=1\),得\(2*5*7*(5*7)^{-1}(mod 3)+3*3*7*(3*7)^{-1}(mod 5)+2*3*5*(3*5)^{-1}(mod 7)\)
  • 这里\(2*5*7*(5*7)^{-1}(mod 3)+3*3*7*(3*7)^{-1}(mod 5)+2*3*5*(3*5)^{-1}(mod 7)=233\),计算乘法逆元参考:求逆元

程序:

#include "stdio.h"
#include "math.h"
int main()
{
    int m, n, x;
    puts("基于费马定理求逆元\n");
    puts("对m * x = 1 mod n,求x\n");
    printf("请输入m=");
    scanf("%d", &m);
    printf("请输入n=");
    scanf("%d", &n);
    x = (int)pow(m, n - 2) % n;
    printf("x=%d\n", x);
    return 0;
}
  • 又因为\(233+k*3*5*7=233+k*105\)\(k\)为任意整数)都满足方程组,取\(k=-2\),得到小于\(3*5*7=105\)的唯一解\(23\)
  • 方程组的唯一解构造如下:\([2*5*7*(5*7)^{-1}(mod 3)+3*3*7*(3*7)^{-1}(mod 5)+2*3*5*(3*5)^{-1}(mod 7)](mod 3*5*7)\)

将上述构造方法推广到一般形式,就是中国剩余定理

定理

设整数\(m_1,...,m_k\)是两两互素的正整数,其中\(M=\prod_{i=1}^{k} m_{i}\),其中方程组:

\[\left\{\begin{array}{l} a_{1}\left(\bmod m_{1}\right) \equiv x \\ a_{2}\left(\bmod m_{2}\right) \equiv x \\ \vdots \\ a_{k}\left(\bmod m_{k}\right) \equiv x \end{array}\right.\]

对模\(M\)有唯一解:\(x \equiv\left(\frac{M}{m_{1}} e_{1} a_{1}+\frac{M}{m_{2}} e_{2} a_{2}+\cdots+\frac{M}{m_{k}} e_{k} a_{k}\right)(\bmod M)\),其中\(e_i\)满足\(\frac{M}{m_{i}} e_{i} \equiv 1\left(\bmod m_{i}\right)(i=1,2, \cdots, k)\)

举例

求解以下方程组:

\[\left\{\begin{array}{l} x \equiv 1 \bmod 2 \\ x \equiv 2 \bmod 3 \\ x \equiv 3 \bmod 5 \\ x \equiv 5 \bmod 7 \end{array}\right.\]

解题过程:

  • 计算\(M=2*3*5*7=210\)
    • \(M_1=M/m_1=105\) ,进而求出\(e_1=M_1^{-1}(mod 2)=1\)
    • \(M_2=M/m_2=70\),,进而求出\(e_2=M_2^{-1}(mod 3)=1\)
    • \(M_3=M/m_3=42\),进而求出\(e_3=M_3^{-1}(mod 5)=3\)
    • \(M_4=M/m_4=30\),进而求出\(e_4=M_4^{-1}(mod 7)=4\)
  • 所以:\(x(mod 210)=(105*1*1+70*2*1+42*3*3+30*5*4)mod 210=173\)

程序

/*中国剩余定理
输入一个n,表示同余式组的同余式的个数
然后依次输入 b[1],...,b[n](即上面的a[i])
以及m[1],...,m[n]
计算 mm=m[1]*...*m[n]
计算M[1] =mm/m[1]...M[n]=mm/m[n];
求M[i]模m[i]的逆元:ni[i]=NI(M[i],m[i]) 
 最后计算x≡ ( ni[1]*M[1]*b[1]+...+ni[n]*M[n]*b[n] )% mm
*/

/*
4
1 5 4 10
5 6 7 11


结果:
x ≡ 2111 (mod 2310)

*/
#include<stdio.h>
#include "math.h"
int NI(int a,int m);

int main()
{
    printf("-------------<中国剩余定理求同余式组的解 >-----------------------\n\n");
    printf("\n说明:\n第一行输入同余式组的同余式的个数n(<20)\n");
    printf("第二行依次输入n个b[i]的值\n");
    printf("第三行依次输入n个m[i]的值\n");
    printf("_________________________________________________________________\n\n");
    int n;
    int b[20]={0},m[20]={0},M[20]={0},ni[20]={0};
    scanf("%d",&n);
    getchar();
    int i;
    //为数组b【】和m【】赋值
    for(i=1;i<=n;++i)
    {
        scanf("%d",&b[i]);
        getchar();
    }
    for(i=1;i<=n;++i)
    {
        scanf("%d",&m[i]);
        getchar();
    }
    //打印同余式组
    printf("\n求解的同余式组为:"); //
    for(i=1;i<=n;++i)//
    {//
        printf("\n\tx ≡ %5d (mod %5d)",b[i],m[i]);//
    }//
    //计算mm
    printf("\n\n  1、计算mm\n\t mm=1") ; //
    int mm = 1;
    for(i=1;i<=n;++i)
    {
        mm = mm*m[i];
        printf("*%d",m[i]); //
    }
    //计算M[i]
    printf("\n\n  2、计算M[i]:") ; //
    for(i=1;i<=n;++i)
    {
        M[i] = mm/m[i];
        printf("\n\t M[%d] = mm/m[%d] = %5d / %5d = %5d",i,i,mm,m[i],M[i]); //
    }
    //求逆
    printf("\n\n  3、求M[i]模m[i]的逆元: 即求解ni[i]*M[i] ≡ 1 (mod = m[i])中的ni[i]") ; //
    for(i=1;i<=n;++i)
    {
        ni[i] = NI(M[i],m[i]) ;
        printf("\n\t ni[%d]:   %5d %5d≡ 1(mod %5d) ",i,ni[i],M[i],m[i]) ;//
    }
    //求解X
    printf("\n\n  4、求X = (Σni[i]*M[i]*b[i])(mod mm) , 1<= i <=n") ; //
    int X = 0;
    printf("\n\t X = 0");//
    for(i=1;i<=n;++i)
    {
        X = X + ni[i]*M[i]*b[i] ;
        printf("+%d*%d*%d",ni[i],M[i],b[i]);//
    }
    X = X%mm;
    printf(" (mod %d)",mm);//
    //输出结果
    printf("\n\n因此,最后的结果为:");
    printf("\n\t--------   x ≡ %d (mod %d)  ---------\n\n",X,mm);
    return 0;
}


int NI(int a,int m)
{
    return (int) pow(a,m-2)%m;
//    int s[100]={0};
//    int t[100]={0};
//    int q[100]={0};
//    int r[100]={0};
//    s[0] = 1 ; s[1] = 0 ;
//    t[0] = 0 ; t[1] = 1 ;
//    r[0] = a ; r[1] = m ;
//    q[1] = r[0] / r[1] ;
//    r[2] = r[0] % r[1] ;
//    r[3] = r[1] % r[2];
//    int j=2;
//    while(1)
//    {
//        q[j]   = r[j-1] / r[j];
//        r[j+1] = r[j-1] - q[j]*r[j];
//        s[j]   = s[j-2] -  q[j-1]*s[j-1] ;
//        t[j]   = t[j-2] -  q[j-1]*t[j-1] ;
//        if(r[j+1]==0) break;
//        j++;
//    }
//    if(s[j]<0) return (m+s[j]);
//    return s[j];
}

应用

  • 在一个大数\(A\)\(M=\prod_{i=1}^{k} m_{i}\)的情况下,可以通过拆分为多个小数\((a_1,...,a_k)\)计算,其中\(a_i=A(mod m_i)(i=1,2, \cdots, k)\),即:

\[(A+B) \bmod M \leftrightarrow\left(\left(a_{1}+b_{1}\right) \bmod m_{1}, \cdots,\left(a_{k}+b_{k}\right) \bmod m_{k}\right) \]

\[(A-B) \bmod M \leftrightarrow\left(\left(a_{1}-b_{1}\right) \bmod m_{1}, \cdots,\left(a_{k}-b_{k}\right) \bmod m_{k}\right) \]

\[(A \times B) \bmod M \leftrightarrow\left(\left(a_{1} \times b_{1}\right) \bmod m_{1}, \cdots,\left(a_{k} \times b_{k}\right) \bmod m_{k}\right) \]

扩展

多项式上的CRT

原理

img

举例

img

img

img

img

img

总结

img

img

img

参考

1、现代密码学-杨波

posted @ 2021-07-09 10:23  PamShao  阅读(744)  评论(0编辑  收藏  举报