中国剩余定理(CRT)

背景


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

  1. 找出三个数:从35的公倍数中找出被7除余1的最小数15,从37的公倍数中找出被5除余1 的最小数21,最后从57的公倍数中找出除31的最小数70
  2. 15乘以22为最终结果除以7的余数),用21乘以33为最终结果除以5的余数),同理,用70乘以22为最终结果除以3的余数),然后把三个乘积相加(152+213+702)得到和233
  3. 233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。

就这么简单。我们在感叹神奇的同时不禁想知道古人是如何想到这个方法的,有什么基本的数学依据吗?

分析


 我们将“孙子问题”拆分成几个简单的小问题,从零开始,试图揣测古人是如何推导出这个解法的。

  首先,我们假设n1是满足除以32的一个数,比如258等等,也就是满足3k+2(k0)的一个任意数。同样,我们假设n2是满足除以53的一个数,n3是满足除以72的一个数。

  有了前面的假设,我们先从n1这个角度出发,已知n1满足除以32,能不能使得 n1+n2 的和仍然满足除以32?进而使得n1+n2+n3的和仍然满足除以32

  这就牵涉到一个最基本数学定理,如果有a%b=c,则有(a+kb)%b=c(k为非零整数),换句话说,如果一个除法运算的余数为c,那么被除数与k倍的除数相加(或相减)的和(差)再与除数相除,余数不变。这个是很好证明的。

  以此定理为依据,如果n23的倍数,n1+n2就依然满足除以32。同理,如果n3也是3的倍数,那么n1+n2+n3的和就满足除以32。这是从n1的角度考虑的,再从n2n3的角度出发,我们可推导出以下三点:

      1. 为使n1+n2+n3的和满足除以32n2n3必须是3的倍数。
      2. 为使n1+n2+n3的和满足除以53n1n3必须是5的倍数。
      3. 为使n1+n2+n3的和满足除以72n1n2必须是7的倍数。

  因此,为使n1+n2+n3的和作为“孙子问题”的一个最终解,需满足:

      1. n1除以32,且是57的公倍数。
      2. n2除以53,且是37的公倍数。
      3. n3除以72,且是35的公倍数。

  所以,孙子问题解法的本质是从57的公倍数中找一个除以32的数n1,从37的公倍数中找一个除以53的数n2,从35的公倍数中找一个除以72的数n3,再将三个数相加得到解。在求n1n2n3时又用了一个小技巧,以n1为例,并非从57的公倍数中直接找一个除以32的数,而是先找一个除以31的数,再乘以2

  这里又有一个数学公式,如果a%b=c,那么(ak)%b=a%b+a%b++a%b=c+c++c=kc  (k>0),也就是说,如果一个除法的余数为c,那么被除数的k倍与除数相除的余数为kc。展开式中已证明。

  最后,我们还要清楚一点,n1+n2+n3只是问题的一个解,并不是最小的解。如何得到最小解?我们只需要从中最大限度的减掉掉357的公倍数105即可。道理就是前面讲过的定理“如果a%b=c,则有(akb)%b=c”。所以(n1+n2+n3)%105就是最终的最小解。

解法


设正整数m1,m2mk两两互素,则同余方程组

xa1(mod m1)
xa2(mod m2)
xa3(mod m3)
   
xak(mod mk)

 有整数解。并且在模M=m1m2mk下的解是唯一的,解为

                              x(a1M1M11+a2M2M21+akMkMk1)mod M

其中Mi=M/mi,而M11Mimi的逆元。

Code

复制代码
int CRT(int a[],int m[],int n) {
    int M = 1, ans = 0;
    for(int i = 1; i <= n; i++) M *= m[i];
    for(int i = 1; i <= n; i++) {
        int x, y;
        int Mi = M/m[i];
        exgcd(Mi, m[i], x, y);
        ans = (ans+Mi*x*a[i])%M;
    }
    if(ans<0) ans += M;
    return ans;
}
View Code
复制代码

 

 


参考文章:
https://www.cnblogs.com/walker01/archive/2010/01/23/1654880.html

https://blog.csdn.net/acdreamers/article/details/8050018

posted @   sparkyen  阅读(502)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示