中国剩余定理

复习中国剩余定理(CRT)

起源#

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

问题转换为求解方程组:

{x2mod3x3mod5x2mod7

求解思路:

  • 先将余数写成和的形式:2+3+2
  • 为了满足第一个方程,即模3,消去后两项,给后两项乘3,得2+33+23
    • 为了满足第二个方程,即模5,消去后第一项和第三项,给乘5,得25+33+235
    • 为了满足第三个方程,即模7,消去后前两项,给乘7,得257+337+235
  • 将结果带入第一个方程,得到257,为了消去57,给乘(57)1(mod3)=1,得257(57)1(mod3)+337+235
    • 将结果带入第二个方程,得到337,为了消去37,给乘(37)1(mod5)=4,得257(57)1(mod3)+337(37)1(mod5)+235
    • 将结果带入第三个方程,得到235,为了消去35,给乘(35)1(mod7)=1,得257(57)1(mod3)+337(37)1(mod5)+235(35)1(mod7)
  • 这里257(57)1(mod3)+337(37)1(mod5)+235(35)1(mod7)=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+k357=233+k105k为任意整数)都满足方程组,取k=2,得到小于357=105的唯一解23
  • 方程组的唯一解构造如下:[257(57)1(mod3)+337(37)1(mod5)+235(35)1(mod7)](mod357)

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

定理#

设整数m1,...,mk是两两互素的正整数,其中M=i=1kmi,其中方程组:

{a1(modm1)xa2(modm2)xak(modmk)x

对模M有唯一解:x(Mm1e1a1+Mm2e2a2++Mmkekak)(modM),其中ei满足Mmiei1(modmi)(i=1,2,,k)

举例#

求解以下方程组:

{x1mod2x2mod3x3mod5x5mod7

解题过程:

  • 计算M=2357=210
    • M1=M/m1=105 ,进而求出e1=M11(mod2)=1
    • M2=M/m2=70,,进而求出e2=M21(mod3)=1
    • M3=M/m3=42,进而求出e3=M31(mod5)=3
    • M4=M/m4=30,进而求出e4=M41(mod7)=4
  • 所以:x(mod210)=(10511+7021+4233+3054)mod210=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];
}

应用#

  • 在一个大数AM=i=1kmi的情况下,可以通过拆分为多个小数(a1,...,ak)计算,其中ai=A(modmi)(i=1,2,,k),即:

(A+B)modM((a1+b1)modm1,,(ak+bk)modmk)

(AB)modM((a1b1)modm1,,(akbk)modmk)

(A×B)modM((a1×b1)modm1,,(ak×bk)modmk)

扩展#

多项式上的CRT#

原理#

img

举例#

img

img

img

img

img

总结#

img

img

img

参考#

1、现代密码学-杨波

作者:Hang Shao

出处:https://www.cnblogs.com/pam-sh/p/14988646.html

版权:本作品采用「知识共享」许可协议进行许可。

声明:欢迎交流! 原文链接 ,如有问题,可邮件(mir_soh@163.com)咨询.

posted @   PamShao  阅读(801)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2020-07-09 linux:配置NTP
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu