中国剩余定理(孙子定理)

中国剩余定理,也称孙子定理,是中国古代求解一次同余式组的重要方法。

《孙子算经》里面的"物不知数"说的是这样的一个题目:一堆东西不知道具体数目,3个一数剩2个,5个一数剩3个,7个一数剩2个,问一共有多少个。
书里面给了计算过程及答案:70*2 + 21*3 + 15*2 -105*2 = 23。

它的计算思路如下:
70是能被5或7整除的数字,但是除以3正好余1。
21是能被3或7整除的数字,但是除以5正好余1。
15是能被3或5整除的数字,但是除以7正好余1。
所以若有数N = 70 * N1 + 21 * N2 + 15 * N3(其中N,N1,N2,N3为正整数),则整数N是符合题目要求的结果(mod3为N1,mod5为N2, mod7为N3)。
我们把N1赋值2,N2赋值3,N3赋值2。
则: N = 70*2 + 21*3 + 15*2 = 233。
但是3,5,7的最小公倍数为105。
所以N + 105*M均为正解。
因此为了求得最小正整数解,我们需要用(N mod 105)也就是23了。

而中国剩余定理就是解决上述问题的通用方法,模板如下。

#include <iostream>
#include <cstdio>
using namespace std;


int exgcd(int a, int b, int &x, int &y)//拓展欧几里得 (可处理负数版本)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	int ans = exgcd(b, a%b, x, y);
	int TT = x;
	if (a*b < 0)
	{
		x = -y;
		y = a / b*y - TT;
	}
	else
	{
		x = y;
		y = TT - a / b*y;
	}
	return ans;//ans是a,b的最大公约数  
}

int chinasolve(int a[],int m[],int k)//除数即m数组必须都是素数
{
	int mm = 1;
	int result = 0;
	for (int i = 0; i < k; i++)
	{
		mm *= m[i];
	}
	for (int j = 0; j < k; j++)
	{
		int L, J;                     // Ni%mi=1↓     ↓因为Ni能被m1, m2,...,mi-1, mi+1,...,mk整除
		exgcd(mm / m[j], -m[j], L, J);// mi*J + 1  =  mm/mi *L  ==> (-mi)*J + mm/mi*L = 1
		result += (m[j] * J + 1)*a[j];//70*2 + 21*3 + 15*2 = 233。
	}
	return (result%mm + mm) % mm;//此处mm实际上是最小公倍数,但是因为m数组都是素数,所以最小公倍数就是mm
}


a数组里存的是所要求的余数,m数组则是对应的除数。例如文章开头那道题,调用函数的方法如下。

int main()
{  
    int a[3] = {2, 3, 2};  
    int m[3] = {3, 5, 7};      
    cout<<"结果:"<<calSYDL(a, m, 3)<<endl;  
}  
输出的结果便是满足题目要求的最小数,23。



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值