中国剩余定理 && 扩展中国剩余定理 -- 个人理解

贴一个大神的数论知识点整理博客:https://blog.youkuaiyun.com/WhereIsHeroFrom/article/details/111693538

中国剩余定理

参考:中国剩余定理(孙子定理)
参考:中国剩余定理(详解)
参考:中国剩余定理算法详解(余数互质和不互质)

注:理解时按上面的顺序看博客

假设( a[] 为模 ,b[] 为余数)( a[] 互质情况下)
x % a1 = b1
x % a2 = b2
x % a3 = b3
求x

由 a2*a3*k 为  x%a1余1的最小的数 (除a1外的a2 a3的符合要求的最小公倍数)

得 a2*a3*k*b1 为 x%a1 余 b1 的最小的数 (对应上面第一个式子) 

那么 x = a2*a3*k1*b1 + a1*a3*k2*b2 + a1*a2*k3*b3 -(a1*a2*a3)*k4

a2*a3*k1 的结果可由exgcd求得:
a2*a3*k ≡ 1(mod a1)
用 m 表示a2*a3, ,用n表示a1
就可以写成  m*k ≡ 1 (mod n)
也就是  m*k - n*y = 1
而 m 的倍数 我们不确定所以式子:
k*m - y*n =1 (扩展欧几里得)
求出上式 k,就能求出 a2*a3*k

x = xx_exgcd(a2*a3*k, a1, xx, yy) * a2*a3*b1 + ...
//互质的中国剩余定理

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;

int exgcd(int a, int b, int &x, int &y)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int Gcd = exgcd(b, a % b, x, y);
    int t = y;
    y = x - (a / b) * y;
    x = t;
    return Gcd; //a b的最大公因数
}

int math(int a[], int m[]) // a[]为余数 ,m[]为除数
{
    int x = 0;
    int cj = 1;
    for (int i = 0; i < n; i++)
        cj *= m[i];

    for (int i = 0; i < n; i++)
    {
        int t = cj / m[i], xx, yy;
        exgcd(t, m[i], xx, yy);

        x = (x + t * xx % cj * a[i]) % cj;
    }

    return (x % cj + cj) % cj;
}

signed main()
{
    int a[15], b[15]; //a模 b余数
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> a[i] >> b[i];

    cout << math(b, a) << endl;

    return 0;
}

扩展中国剩余定理 (不互质)

拓展中国剩余定理

将式子合并(以前两个为例):

 x = a1 mod m1
 x = a2 mod m2
 -> k1m1 - k2m2 = a2 - a1 
 -> k1m1 = a2 -a1 mod(m2)
上式有解时: gcd(m1,m2) | (a2 -a1),即(a2-a1)能整除m1和m2的最大公约数

令 d = gcd(m1,m2) ,c = a2 - a1
-> k1m1 = c mod (m2)
将上式化简(去掉式子的最大公约数):k1m1/d = c/d mod(m2/d)

下一步是求出k1的值,带入第三行的式子
化简式子后,可发现两个式子通过一系列代换可合并为一个(式子中的变量不同)

求 k1 用到了(m1/d)关于(m2/d)的逆元
-> k1 = 逆元*(c/d)     ----------------(c/d在上段最后一行)

求逆元:
由 ax = 1 mod(b)
-> m1/d ≡ 1 mod(m2/d)
利用扩展欧几里得: ax + by = gcd(a,b)
将 a看作(m1/d) , b看作(m2/d) :
也就是 (m1/d)*x + (m2/d)*y = 1
-> 逆元xx 就是 exgcd(m1/d,m2/d,xx,yy) 式子中的xx(记为 K)(注:m1/d和m2/d互质)

求k1:
k1 = K*(c/d) mod(m2/d) = k*(c/d) + y*(m2/d)

将k1带入第一个式子:x = a1 mod(m1)
-> x = k1m1 + a1
-> x = ( K*(c/d) mod(m2/d) )*m1 + a1
-> x =  K*(c/d)*m1 + a1  mod(m1*m2/d)

与第一个式子对比:x = a mod m
通式:a = K*(c/d)*m1+a1 , m = m1*m2/d 
依次类推...

扩展中国剩余定理模板题:
204. 表达整数的奇怪方式 : https://www.acwing.com/problem/content/description/206
P1495 【模板】中国剩余定理(CRT)/曹冲养猪:https://www.luogu.com.cn/problem/P1495

#include <bits/stdc++.h>
using namespace std;

long long a[30],m[30];

long long exgcd(long long  a,long long  b,long long  &x,long long &y)
{
     if (b == 0)
     {
          x = 1;
          y = 0;
          return a;
     }
     long long Gcd = exgcd(b, a % b, x, y);
     long long t = y;
     y = x - (a / b) * y;
     x = t;
     return Gcd; //a b的最大公因数
}

int main()
{
     int n;
     cin>>n;
     for(int i=0;i<n;i++)
     cin>>m[i]>>a[i]; // m是模 ,a是余数!!

     long long a1,a2,m1,m2,c,xx,yy;

     a1=a[0],m1=m[0];

     for(int i=1;i<n;i++)
     {
          a2=a[i],m2=m[i];

          c= a2- a1;

          long long gcd=__gcd(m1,m2);

          if( c % gcd !=0)
          {
               cout<<"-1"<<endl;
               return 0;
          }

          exgcd(m1/gcd,m2/gcd,xx,yy);

          xx = c / gcd * xx;
          xx = (xx% (m2/gcd) +(m2/gcd)) %(m2/gcd); //化为正数
          a1 += xx  *  m1 ;
          m1 =  m2 /gcd * m1 ;
     }
     cout<<a1<<endl;

     return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值