zoukankan      html  css  js  c++  java
  • 中国剩余定理

    小学奥数中我们学过一个经典的问题:有一个数除以3余2,除以5余3,除以7余5,求这个数。 这就是著名的中国剩余定理

    中国剩余定理:

      我们就先来考虑上述的这个问题。不妨先设三个整数n1,n2,n3满足:n1%3=2,n2%5=2,n3%7=2。接下来,对于n1而言,如果使得n1满足n1%5=0并且n1%7=0,那么(n1+n2)%5=n2%5=2并且(n1+n3)%7=2

    这个不难说明,因为(n1+n2)%5=((n1%5)+(n2%5))%5,因为我们使得n1%5=0,所以((n1%5)+(n2%5))%5=(0+(n2%5))%5。至此,我们不难发现如果使得n2,n3有类似的性质(即:n2%3=0并且n2%7=0,n3%3=0并且n3%5=0),那么(n1+n2+n3就会同时满足题设中三个要求。这样看来,我们要求的就是n1,n2,n3这样的数,并把它们相加。整理一下上述n1,n2,n3满足的条件即:(以n1为例)

    1. n1%3=2(找一个数满足题设中的某一个条件)

    2. n1%5=0并且n1%7=0(找到的这个数同时也是其它所有数的公倍数)

    值得注意的是,题目中的任意两个模数都是互质的,所以要求几个模数的最小公倍数的时候,只需要将它们相乘即可。

    综上所述,我们可以将这个简单的问题变成一个求解数学问题的通法:

      求出一个整数x满足: ,其中m1,m......mn任意两数互质。

    那么就有:方程组  的通解形式为

     

    下面讲一下具体的代码实现:

    1. 依次考虑每个模数,对于某个模数mi,我们首先要找一个其他所有模数的公倍数x,满足题设中的x%mi=restiresti表示题设中规定的那个余数)

    2. 找最小公倍数xi的方法:因为其他所有模数任意两个都互质,所以最小公倍数就是其他所有模数的乘积。可以先预处理计算出M=(m1*m2*......mn,也就是先把所有模数都乘起来,那么xi=M/mi

    3. 最小公倍数xi不一定满足xi%mi=resti,那我们就在公倍数里找,即存在(p*xi)%mi=resti,而关键就在求出p*xi是多少。

    4. p*xi可以通过求解同余方程p*x≡ resti(mod mi求解。根据求解线性同余方程的方法,我们设p*xi-resti=(-q)*mi 然后exgcd求解即可。

    5. 求解exgcd具体细节:写成标准一次式:xi*p+mi*q=resti,该等式一定有解,因为xi=M/mi且任意两个模数互质,所以gcd(xi,mi)=1,所以该方程有解。根据exgcd,我们先求出了xi*p+mi*q=gcd(xi,mi)=1的一组特解,再用解得的pxi再乘resti即为在xi*p+mi*q=resti方程成立情况下的p*xi,这就是我们要求的那个公倍数。

    6. 按照上面的步骤,我们求出了一个满足题设某一条件的整数,然后继续考虑下一个条件。步骤相同,只需把每一步求出的整数加起来就是最后结果。

    7. 如果题目是让我们求出满足条件的最小正整数,我们则需要对于每步的答案%M即可。

    具体代码如下:

    #include<cstdio>
    int n,m[101],rest[101],M=1; 
    int exgcd(int xi,int mi,int &p,int &q)//扩展欧几里得算法 
    {
        if (mi==0) {p=1; q=0; return xi;}
        int d=exgcd(mi,xi%mi,p,q);
        int z=p; p=q; q=z-(xi/mi)*q;
        return d;
    }
    int CR()//中国剩余定理 
    {
        int sum=0,p,q;
        for (int i=1;i<=n;i++)//依次考虑每一个题设 
        {
            int xi=M/m[i];//求出其余模数的最小公倍数 x
            exgcd(m[i],xi,q,p);//求解:xi*p+mi*q=gcd(xi,mi)=1
            sum=(sum+xi*p*rest[i])%M;//确保数最小 
        }
        return (sum%M+M)%M;
    }
    int main()
    {
        scanf ("%d",&n);//输入n个题设条件
        for (int i=1;i<=n;i++)
        {
            scanf ("%d%d",&m[i],&rest[i]);//输入n个模数和余数 
            M*=m[i];//提前计算所以模数的积,为后面求最小公倍数做准备 
        }
        printf("%d",CR());
        return 0;
     } 

     

    中国剩余定理针对于任意两个模数都互质,那么对于不都互质的同余方程组,也有求解方法:  //POJ 2891 

    题目:给定2n个整数:a1,a2 ...... an , m1,m2 ...... mn ,求一个最小正整数,满足:对于1<=i<=n,x≡ai(mod mi),或给出无解。

    思路:因为不满足模数两两互质,中国剩余定理不再适用,我们考虑归纳法:假设前k-1个方程已经求出解x,M是前k-1个模数的乘积。那么前k-1个方程的通解就是x+i*M。接下来我们考虑第k个方程:求出一个t使得:x+t*M≡ak(mod mk),t*M≡ak-x(mod mk,然后就是exgcd求解这个同余方程,有解则满足(ak-x)% gcd(t*M,mk)== 0,否则输出-1无解。并且更新前k个方程的解,即为:x'=x+t*M 。 这里我们需要弄清楚M的作用,即是前面所有模数的公倍数,使得我们当前枚举的这个数不会对前面的条件产生影响。所以在具体操作来更新M的时候,我们要保证求出的整数最小的要求Mk-1更新到Mk时,我们不是直接*mk,而是乘mk再除以dexgcd求出的Mmk的最大公约数),主要因为Mmk不一定互质,M中可能有mk的因子,所以把它们共同因子除去不会影响结果,即:保证了M的最小性和不会对前面的条件产生影响的性质。另外,x+t*M≡ak(mod mk这个式子也就告诉我们,t<=mk,所以对于求解出来的t要对mk取模,保证最小。

    代码如下:

    #include<cstdio>
    typedef long long ll;
    ll rest[20005],a[20005];
    ll X,M,n;
    ll exgcd(int a,int b,int &x,int &y)
    {
        if (b==0) {x=1; y=0; return a;}
        ll d=exgcd(b,a%b,x,y);
        ll z=x; x=y; y=z-y*(a/b);
        return d;
    }
    ll work()
    {
        M=a[1],X=rest[1];
        int x,y;
        for (int i=2;i<=n;i++)
        {
            ll d=exgcd(M,a[i],x,y);
            if ((rest[i]-X)%d!=0) return -1;
            x=(rest[i]-X)/d*x%a[i];
            X+=(x*M);
            M=M/d*a[i];
            X%=M;
        }
        return (X%M+M)%M;
    }
    int main()
    {
        while (scanf ("%lld",&n)!=EOF)
        {
            for (int i=1;i<=n;i++)
                  scanf ("%lld%lld",&a[i],&rest[i]);
            printf("%lld
    ",work());
        }
        return 0;
     } 
  • 相关阅读:
    C# 中国日历 农历 阳历 星座 二十四节气 二十八星宿 节日 天干地支
    C# AutoMapper:流行的对象映射框架,可减少大量硬编码,很小巧灵活,性能表现也可接受。
    Asp.net webapi 判断请求参数是否为空简易方法 Model Validation 判断请求参数是否为空
    IIS本地部署局域网可随时访问的项目+mysql可局域网内访问
    jquery ajax return不起作用
    asp.net webapi 给字段赋初始值DefaultValue 解决前端传空字符串后台接受不是“”而是NULL
    git log的常用命令
    C++面试题集合(持续更新)
    python的with用法(转载)
    stage_ros的world文件配置方法
  • 原文地址:https://www.cnblogs.com/zylAK/p/9581919.html
Copyright © 2011-2022 走看看