中国剩余定理&扩展中国剩余定理

1.中国剩余定理的推导 ≡


 学习引用:

            同余方程:x≡a(mod b)   <=> x(mod b)=a(mod b) <=>  x%b=a%b;

            乘法逆元:如果有a/b≡a*x(mod p)

                              我们可以等价变换为:

                       a/b=a*b^(-1)(表示逆元)=a/b*b*b^(-1)(mod p)

                       化简可得:b^b^(-1)≡1(modp) 

                       则由费马小定理可得到 b^(-1)=b^(p-2);

                       由于 扩展欧几里得算法求逆元可以得到:

                                exgcd(a,b,x,y)中x表示a在a(mod b)下的乘法逆元 

              数学知识:x/3 余 1

                                 2*x/3 余 2   

问题背景进行推论:

   在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”。

 问题告诉我们了:
              假设一个数是x,那么这个x满足:

                                        1.  x%3=2; 2. x%5=3;  3.x%7=2; 

              我们可以写成:x=3*k+2;x=5*k+ 3;x=7*k+2;

              让我们求得最小的x是多少;(-^<>^-)

 问题求解:

               x=3*k+2; x=5*k+3; x=7*k+2;

               从这三个公式进行入手我们可以把问题分解为:

               现在 1.x是5和7的倍数并且除以3余下2

                        2.x是3和7的倍数并且除以5余下3

                        3.x是3和5的倍数并且除以7余下2

              这三个问题的和是和总问题等价的;

             那么我们让M=3*5*7;

             当解决第一个问题时我们让M1= M/3;

             那么M1就是5和7的倍数了,之后我们求他除以3余下2的

             我们现在求得数假设为 Mi*k

             那么有Mi*k(mod 3)=2 <=> Mi*k ≡ 2(mod 3);

             Q:不会求;A:我也不会……

             我们知道的是b^b^(-1) ≡1(mod p)

             那么 Mi^Mi^(-1) ≡1(mod p);

             那么我们求出Mi ^(-1) *2不就是k吗

             问题一的答案不就是 Mi^(-1) *2 *Mi 吗

             随着问题一的解决后面的解法就迎刃而解了,真不错

            那么对于更一般的呢我们就要发现:

                    对于多组的同余问题:

                            x ≡ a1(mod m1)

                            x ≡ a2(mod m2)

                            x ≡ a3(mod m3)

                                ……………

            令M=m1*m2*m3……;

               Mi=M/mi; 

               ans=Mi*Mi^(-1)*ai;

               求和;最后是最小所以还要取余他们的乘积

               最后上代码:

题目:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <bitset>
using namespace std;
#define int long long
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const long long LLINF = 4e18 + 5;
typedef long long LL;
typedef pair<int, int> PII;
#define xx first
#define yy second
#define endl '\n'

struct node
{

    //	bool operator<(const node &a) const
    //	{
    //	}
};
void ClearFloat()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}

int read()
{
    int ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int n,a[N],m[N],Mi[N],M=1,ans=0;
int exgcd(int a, int b, int &x, int &y)
{
    if (b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int z = x;
    x = y;
    y = z - y * (a / b);
    return d;
}
signed main()
{
    ClearFloat();
    cin>>n;
    for(int i=1;i<=n;i++)
    { 
        cin>>m[i];
        M*=m[i];  
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        Mi[i]=M/m[i];
        int x=0,y=0;
        exgcd(Mi[i],m[i],x,y);
        ans+=a[i]*Mi[i]*(x%m[i]+m[i]);
    }
    cout<<ans%M;
}

 2.扩展中国剩余定理

中国剩余定理的局限性:

           模数必须是满足两两互质,对于这种情况我们要想办法得到一个更普遍的算法。那么扩展中国剩余定理便得以应用。

           首先我们回到最开始的问题:

问题背景进行推论:

   在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?。

学习引用:

     1.乘法逆元的欧几里得算法。

     2.欧几里得算法里的:&x与&y 表示的是 ax+by=gcd(a,b)的一组解;

        那么如果求ax+by=c的一组解的话 其有解当且仅当 gcd(a,b)|c,c能把gcd(a,b)整除掉 

  那么就有ax+by=k*gcd(a,b),那么我们知道x0,y0是ax+by=gcd(a,b)的一组解时那么他的特解就是k*x0,ky0;k=c/gcd(a,b);那么通解

                    x=k*x0+w*b/gcd(a,b),y=c/d-w*a/gcd(a,b);w是一个未知数

 

 问题求解:

      我们已经知道了

                  x=a1*k1+b1; x=a2*k2+b2; x=a3*k3+b3;

      我们两两进行观察:

                              x=a1*k1+b1=a2*k2+b2;

       对a1*k1+b1=a2*k2+b2进行整理可以得到:

                                 a1*k1-a2*k2=b2-b1;

这个公式和上面的ax+by=c是形式相近的;

              我们求exgcd(a1,-a2,&x,&y)可以求出a1*k1-a2*k2=gcd(a1,-a2)的 一组解

              我们求得通解为:

                             1.若(b2-b1)%gcd(a1,-a2) ! = 0  =>无解

                             2.k1=k11*(b2-b1)/(gcd(a1,-a2));

                                 k2=k22*(b2-b1)/(gcd(a1,-a2));

                             3. 我们知道:

                                       k1=k11+k*a2/gcd(a1,-a2)

                                       k2=k22+k*a1/gcd(a1,-a2)

                                 不妨让k11=k1%abs(a2/gcd(a1,-a2))

                                           k22=k2%abs(a1/(gcd(a1,-a2));

                           x=a1*(k11+k*(b2-b1)/(gcd(a1,-a2)))+b1

                             =k11*a1+m+k*lcm(a1,a2)

                                让a1=lcm(a1,a2),b=k11*a1+b1

                  (其中通过d=exgcd(a,b,x,y),lcm(a1,a2)=a1*a2/d;k11=x*(b2-b1)/d%abs(a2/d))

                         再次变为x=k*a1+b的形式不断轮回

题目:AcWing 204. 表达整数的奇怪方式 - AcWing

代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <bitset>
using namespace std;
// #define int long long
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const long long LLINF = 4e18 + 5;
typedef long long LL;
typedef pair<int, int> PII;
#define xx first
#define yy second
#define endl '\n'

struct node
{

    //	bool operator<(const node &a) const
    //	{
    //	}
};
void ClearFloat()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}

int read()
{
    int ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int n;
int a[N], m[N];

LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int z = x;
    x = y;
    y = z - y * (a / b);
    return d;
}
int main()
{
    ClearFloat();
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> m[i];
    }
    LL m1 = m[1], a1 = a[1];
    for (int i = 2; i <= n; i++)
    {
        LL k1, k2;
        LL d = exgcd(a1, -a[i], k1, k2);
        if ((m[i] - m1) % d)
        {
            cout << "-1";
            return 0;
        }
        k1 = k1 * (m[i] - m1) / d;
        k1 = (k1 % abs(a[i] / d) + abs(a[i] / d)) % (abs(a[i] / d));
        m1 = k1 * a1 + m1;
        a1 = abs(a1 / d * a[i]);
    }
    cout << m1 << endl;
}

仔细揣摩一下才会懂得

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值