中国剩余定理(模数不互质)

上一篇博客写的是中国剩余定理模数互质的情况,然鹅——还存在着模数不互质的情况,而原来的做法就没有办法用了

那我们现在该怎么做呢?


 原来的思路是对于每一个方程,我们找出一个基础数,使得基础数满足该方程要求。因为是互质的关系,所以所有基础数加起来也不会冲突。(这个由最小公倍数保证)

还是这个栗子 存在一个数x,除以3余2,除以5余3,除以7余2,然后求这个数

3的基础数是35,满足除以3余2,而且是5和7的倍数,5,7同理。也就是说:基础数几倍几倍的变化是不会造成突然冒出来一个什么数%5或%7出现了余数


而现在模数不互质,还是举个栗子存在一个数x,除以6余4,除以8余2,除以9余7,然后求这个数

答案手推出来是34,如果仍然按照原来的做法lcm为72,6的基础数为。。。诶诶?怎么推不出来?72/6=12,ok,12不满足,再扩大,emm,还是不满足,再扩大。。。

好吧,相信大家已经发现了!12是6的倍数啊!再怎么扩大也不可能满足条件啊!

这就是互质与不互质的区别:不互质可能会导致基础数是模数的倍数,那么这个算法就凉凉了

那就让我们换一个思维方式吧!

可以先对第一个方程求解出一个满足条件的x,再去看下一个方程,在这个x的基础上,加上一个满足下一个方程的x',同时不破坏前面方程的要求。

也就是说,我们去判断一个方程有没有解,解是多少。这,这不就是扩欧吗?

具体看代码吧~

#include<bits/stdc++.h>
using namespace std;
int a[10],m[10],tong[103];
int exgcd(int a,int &x,int b,int &y)
{
    if(b==0){x=1;y=0;return a;}
    int x2,y2;
    int gcd=exgcd(b,x2,a%b,y2);
    x=y2;
    y=x2-a/b*y2;
    return gcd;
}
int main()
{
    freopen("crt.in","r",stdin);
    freopen("crt.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%d%d",&a[1],&m[1]);
        //用lcm求当前到达过的方程中,所有模数的lcm,sum是最后的结果,但是每一次都会用
        //解出的x去更新它,fail判断是否有解 
        int lcm=m[1],sum=a[1],fail=0;
        for(int i=2;i<=n;i++)
        {
            int x,y;
            scanf("%d%d",&a[i],&m[i]);
            a[i]=((a[i]-sum)%m[i]+m[i])%m[i];//sum就是要求的x,不断更新 
            int d=exgcd(lcm,x,m[i],y);//lcm*x+m[i]*y+sum=a[i]才能保证x%m[i]=a[i] 
            if(a[i]%d==0) x=x*(a[i]/d)%m[i];
            else fail=1;
            sum+=x*lcm;//注意是乘lcm哦!不然可能会出现前面的方程冲突 
            lcm=lcm/d*m[i];//到现在这一个方程了,所有模数的最小公倍数 
            sum=(sum%lcm+lcm)%lcm;
        }
        if(fail) printf("No\n");
        else printf("%d\n",sum); 
    }
}
/*
3
4 6
2 8
7 9

3
2
3 5
4 7
3
4 6
2 8
7 9
2
1 2
2 4
*/

 

posted @ 2019-03-04 22:04  yyys  阅读(2311)  评论(1编辑  收藏  举报