中国剩余定理
复习中国剩余定理(CRT)
起源#
在《孙子算经》中的“物不知数”的问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。
问题转换为求解方程组:
求解思路:
- 先将余数写成和的形式:
- 为了满足第一个方程,即模3,消去后两项,给后两项乘3,得
- 为了满足第二个方程,即模5,消去后第一项和第三项,给乘5,得
- 为了满足第三个方程,即模7,消去后前两项,给乘7,得
- 为了满足第二个方程,即模5,消去后第一项和第三项,给乘5,得
- 将结果带入第一个方程,得到
,为了消去 ,给乘 ,得- 将结果带入第二个方程,得到
,为了消去 ,给乘 ,得 - 将结果带入第三个方程,得到
,为了消去 ,给乘 ,得
- 将结果带入第二个方程,得到
- 这里
,计算乘法逆元参考:求逆元
程序:
#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;
}
- 又因为
( 为任意整数)都满足方程组,取 ,得到小于 的唯一解 - 方程组的唯一解构造如下:
将上述构造方法推广到一般形式,就是中国剩余定理!
定理#
设整数
对模
举例#
求解以下方程组:
解题过程:
- 计算
,进而求出 ,,进而求出 ,进而求出 ,进而求出
- 所以:
程序#
/*中国剩余定理
输入一个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];
}
应用#
- 在一个大数
模 的情况下,可以通过拆分为多个小数 计算,其中 ,即:
扩展#
多项式上的CRT#
原理#
举例#
总结#
参考#
1、现代密码学-杨波
作者:Hang Shao
出处:https://www.cnblogs.com/pam-sh/p/14988646.html
版权:本作品采用「知识共享」许可协议进行许可。
声明:欢迎交流! 原文链接 ,如有问题,可邮件(mir_soh@163.com)咨询.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2020-07-09 linux:配置NTP