中国剩余定理
复习中国剩余定理(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
原理
举例
总结
参考
1、现代密码学-杨波