中国剩余定理(SCAU 1077 韩信点兵)

本文详细介绍了中国剩余定理的概念及其在解决孙子问题上的应用,通过数学公式和具体步骤展示了如何找到满足多个同余条件的最小正整数解。此外,还提供了扩展欧几里得算法的实现,并给出了一个实际应用案例——韩信点兵问题,该问题要求找到一个数在不同质数下除余特定数,满足所有条件的最小正整数。最后,讨论了负数模运算的规则。
摘要由CSDN通过智能技术生成

先介绍一下中国剩余定理
下面会用到的数学公式:
①如果a%b=c,那么如果x%b=c/2,此时x=a/2;也就是说除数相等时,被除数和余数是成比例的。
②如果a%b=c,那么 (a + k*b)%b=c,其中k为整数

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

具体解法分下面三步:

1、找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。

2、用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加15∗2+21∗3+70∗215∗2+21∗3+70∗2得到和233。

3、用233除以3、5、7的最小公倍数105,得到余数23,这个余数23就是符合条件的最小数。

解释: 那么我们可能会问:为什么要这样算……

如果我们设出三个数n1、n2、n3,满足:n1%3=2、n2%5=3、n3%7=2;
那么,我们先从n1这个角度出发,能不能让n1+n2也满足%3=2呢?根据上面的公式②,如果n2是3的倍数就完全可以满足, 同样如果让n1+n2+n3满足%3=2,需要n2和n3都是3的倍数;
同样的,我们从n2和n3的角度出发可以得到:

1、n1需要是5、7的倍数;

2、n2需要是3、7的倍数;

3、n3需要是3、5的倍数;

如果找到了满足上面的三个条件的n1、n2、n3,根据上面的推论,n1+n2+n3就是满足题目要求的那个数,(但不一定是最小的哈)

接下来的问题就是----------------------我们要怎么在5和7的倍数中找出一个数满足%3=2(2、3条件类似)

我们最开始列出的第一个公式还没有用呢!是不是可以转化成在5和7的倍数中找到一个数满足%3=1就可以了呢?然后我们再*2就可以了,为什么会想要让余数为1呢?因为这个跟逆元的求法几乎一样~。

补充:求逆元的方法:

①费马小定理
在这里插入图片描述
②拓展欧几里得
在这里插入图片描述
下面给出模板代码

typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) //扩展欧几里得算法
{
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	ll t = exgcd(b, a % b, y, x);//注意是y,x
	y = y - a / b * x;
	return t;
}
ll china(ll a[],ll b[],int n)//a[]为除数,b[]为余数
{
    ll M=1,ans=0;
    for(int i=0;i<n;++i)  //算出它们累乘的结果
        M*=a[i];
    for(int i=0;i<n;++i)
    {
        ll x,y;
        ll w=M/a[i];
        ll t=exgcd(w,a[i],x,y);  //计算逆元
        ans=(ans+w*b[i]*(x/t))%M; //当w和a[i]互质时,t=gcd(w,a[i])=1
    }
    return (ans+M)%M;
}

另:在倒数第二行的式子中,wb[i](x/t)中,x其实是式子 w*x+a[i]*y=gcd(w,a[i]) 求出来的“逆元”,打引号是因为真正的逆元式子右边应该是1而不是gcd(w,a[i]),因此要把x/t,这时的x/t(t为gcd(w,a[i]))才是真正的逆元,然后我们再乘以余数b[i]*w,得到的就是我们想要的~~

进入正题
1077 韩信点兵
题目描述:相传汉高祖刘邦问大将军韩信统御兵士多少,韩信答说,每3人一列余1人、5人一列余2人、7人一列余4人、13人一列余6人、 17人一列余2人、19人一列余10人、23人一列余1人、29人一列余11人。

刘邦茫然而不知其数。你呢? 你是一位优秀的程序员,请你帮刘邦解决这一问题。

输入格式
要求由键盘输入A,B,C,D,E,F,G,H,a,b,c,d,e,f,g,h十六个数,分别代表每A人一列余a、每B人一列余b、每C人一列余c、每D人一列余D、每E人一列余e、每F人一列余f、每G人一列余g、每H人一列余h,其中A,B,C,D,E,F,G,H为互不相等的质数

输出格式
输出总兵士数,要求输出满足条件的最小的一个,但要满足8种排法的每一种排法至少可排一列。(保证给的数据,有结果且计算的结果不会超过2的63次方)

输入样例
2 3 5 7 11 13 17 19
1 1 1 1 1 1 1 1

输出样例
9699691

AC代码:

#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
ll exgcd(ll a,ll b,ll &x,ll &y)//拓展欧几里得
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll t=exgcd(b,a%b,y,x);//注意是y,x
    y=y-a/b*x;
    return t;
}
ll china(ll a[],ll b[])//中国剩余定理
{
    ll m=1,ans=0,mx=-1;
    for(int i=0;i<8;i++)
    {
        m*=a[i];
        mx=max(mx,a[i]);//记录最大的除数
    }
    for(int i=0;i<8;i++)
    {
        ll x,y;
        ll w=m/a[i];
        ll t=exgcd(w,a[i],x,y);
        ans=(ans+w*b[i]*(x/t))%m;//当w和a[i]互质时,t=gcd(w,a[i])=1
        
    }
    while(ans<mx)ans+=m;//当结果小于最大的除数,不断加m
    return ans;
}
int main()
{
    ll a[8],b[8],ans;
    for(int i=0;i<8;i++)cin>>a[i];//存储除数
    for(int i=0;i<8;i++)cin>>b[i];//存储余数
    cout<<china(a,b)<<endl;
    return 0;
}

PS:总结一下负数的模怎么计算
-9%5=-4
9%-5=4

先看成绝对值取模,再根据被除数的正负性给模后的值加上符号

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值