第三十章 数论——扩展中国剩余定理

第三十章 数论——扩展中国剩余定理

  • 一、中国剩余定理的弊端
  • 二、扩展中国剩余定理
    • 1、作用
    • 2、内容
    • 3、问题
    • 4、代码

一、中国剩余定理的弊端

在第二十九章中,作者详细地讲解了中国剩余定理的使用,在开始本章节的讲解之前,建议读者先去看上一章节的讲解。传送门:中国剩余定理与线性同余方程组

中国剩余定理是用来解决线性同余方程组的,但是有一个很苛刻的要求就是:方程组中的同余方程的模数必须是互质的。只有这样我们才能套用中国剩余定理。

二、扩展中国剩余定理

1、作用

扩展中国剩余定理就是为了解决方程组中模数不互质的情况。

2、内容

{ x ≡ a 1   m o d ( m 1 ) x ≡ a 2   m o d ( m 2 ) x ≡ a 3   m o d ( m 3 ) . . . x ≡ a n   m o d ( m n ) \begin{cases} x\equiv a_1\ mod(m_1)\\ x\equiv a_2\ mod(m_2)\\ x\equiv a_3\ mod(m_3)\\ ...\\ x\equiv a_n\ mod(m_n) \end{cases} xa1 mod(m1)xa2 mod(m2)xa3 mod(m3)...xan mod(mn)
我们取出前两个式子:

x ≡ a 1   m o d ( m 1 ) x ≡ a 2   m o d ( m 2 ) x\equiv a_1\ mod(m_1)\\ x\equiv a_2\ mod(m_2)\\ xa1 mod(m1)xa2 mod(m2)

这两个式子能够写成:
x = k 1 m 1 + a 1 x = k 2 m 2 + a 2 x=k_1m_1+a_1\\ x=k_2m_2+a_2 x=k1m1+a1x=k2m2+a2
两个式子相减:
k 1 m 1 + a 1 = k 2 m 2 + a 2 k_1m_1+a_1=k_2m_2+a_2 k1m1+a1=k2m2+a2
将上面的式子移项:
k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1-k_2m_2=a_2-a_1 k1m1k2m2=a2a1

我们把 k 1 k_1 k1 − k 2 -k_2 k2看作 x k x_k xk y k y_k yk

那么这个式子就可以写成:
x k m 1 + y k m 2 = ( a 2 − a 1 ) x_km_1+y_km_2=(a_2-a_1) xkm1+ykm2=(a2a1)

根据我们的裴蜀定理:

如果 g c d ( m 1 , m 2 ) ∣ ( a 2 − a 1 ) gcd(m_1,m_2)|(a_2-a_1) gcd(m1,m2)(a2a1)

我们就能够根据扩展欧几里德算法计算出系数。

如果上述整除的关系不成立,说明这个式子是无解的。

我们现在来讨论整除关系成立的情况:

我们可以根据欧几里得算法计算出:

x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)的特殊解。

为了得到我们的 x k x_k xk y k y_k yk,我们需要给两遍同乘 ( a 2 − a 1 ) g c d ( m 1 , m 2 ) \frac{(a_2-a_1)}{gcd(m_1,m_2)} gcd(m1,m2)(a2a1)

所以我们的 x x x y y y的特殊解可以写成:

x k 1 = x 0 ∗ ( a 2 − a 1 ) g c d ( m 1 , m 2 ) y k 1 = y 0 ∗ ( a 2 − a 1 ) g c d ( m 1 , m 2 ) x_{k1}=x_0*\frac{(a_2-a_1)}{gcd(m_1,m_2)}\\ y_{k1}=y_0*\frac{(a_2-a_1)}{gcd(m_1,m_2)} xk1=x0gcd(m1,m2)(a2a1)yk1=y0gcd(m1,m2)(a2a1)

但是这是一组特殊解:

当我们式子为:

x 0 m 1 + y 0 m 2 = n ∗ g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=n*gcd(m1,m2) x0m1+y0m2=ngcd(m1,m2)

此时我们可以通过特殊解构造出一般解:

我们构造的原则是下面的等式恒成立

x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)

因此我们给我们的特殊解进行如下变形:

x k = x k 1 + m 2 g c d ( m 1 , m 2 ) ∗ n y k = y k 1 − m 1 g c d ( m 1 , m 2 ) ∗ n x_k=x_{k1}+\frac{m_2}{gcd(m_1,m_2)}*n\\ y_k=y_{k1}-\frac{m_1}{gcd(m_1,m_2)}*n xk=xk1+gcd(m1,m2)m2nyk=yk1gcd(m1,m2)m1n

我们将这两个式子带回: x 0 m 1 + y 0 m 2 = g c d ( m 1 , m 2 ) x_0m_1+y_0m_2=gcd(m1,m2) x0m1+y0m2=gcd(m1,m2)

发现依然是成立的。

所以我们构造的 x k x_{k} xk y k y_{k} yk就是通解

此时带回我们的
x = k 1 m 1 + a 1 x=k_1m_1+a_1 x=k1m1+a1
我们前面是将 k 1 k_1 k1看作了 x k x_k xk,所以我们的式子可以变形为:
x = m 2 ∗ m 1 g c d ( m 1 , m 2 ) ∗ n + x k 1 ∗ m 1 + a 1 x=\frac{m_2*m_1}{gcd(m_1,m_2)}*n+x_{k1}*m_1+a_1 x=gcd(m1,m2)m2m1n+xk1m1+a1

m 2 ∗ m 1 g c d ( m 1 , m 2 ) \frac{m_2*m_1}{gcd(m_1,m_2)} gcd(m1,m2)m2m1就是我们的最小公倍数。因为:

最大公因数 ∗ 最小公倍数 = 两个数的乘积 最大公因数*最小公倍数=两个数的乘积 最大公因数最小公倍数=两个数的乘积

所以我们让:

l c m ( m 1 , m 2 ) = m 2 ∗ m 1 g c d ( m 1 , m 2 ) lcm(m_1,m_2)=\frac{m_2*m_1}{gcd(m_1,m_2)} lcm(m1,m2)=gcd(m1,m2)m2m1

所以我们的式子可以化简为:
x = l c m ( m 1 , m 2 ) ∗ n + x k 1 m 1 + a 1 x=lcm(m_1,m_2)*n+x_{k1}m_1+a_1 x=lcm(m1,m2)n+xk1m1+a1

而这个式子可以写成:

x ≡ r ( m o d   m ) x\equiv r(mod\ m) xr(mod m)

其中:

{ r = k 1 m 1 + a 1 m = l c m ( m 1 , m 2 ) \begin{cases} r={k_1}m_1+a_1\\ m=lcm(m_1,m_2) \end{cases} {r=k1m1+a1m=lcm(m1,m2)

经过上述推导,我们将两个同余方程:

x = k 1 m 1 + a 1 x = k 2 m 2 + a 2 x=k_1m_1+a_1\\ x=k_2m_2+a_2 x=k1m1+a1x=k2m2+a2

合并成了一个同余方程:

x ≡ r ( m o d   m ) x\equiv r(mod\ m) xr(mod m)

因此,我们只需要不断地去合并,经过 n − 1 n-1 n1次合并,我们就能够把 n n n个同余方程合并成一个同余方程。

当只有一个合并方程的时候,我们肯定就能够利用扩展欧几里得算法进行计算了。

3、问题

第三十章 数论——扩展中国剩余定理_第1张图片

4、代码

#include 
#include 
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
int main()
{
    int n;
    cin >> n;
    LL x = 0, m1, a1;
    cin >> m1 >> a1;
    for (int i = 0; i < n - 1; i ++ )
    {
        LL m2, a2;
        cin >> m2 >> a2;
        LL k1, k2;
        LL d = exgcd(m1, m2, k1, k2);
        if ((a2 - a1) % d)
        {
            x = -1;
            break;
        }
        k1 *= (a2 - a1) / d;
        k1 = (k1 % (m2/d) + m2/d) % (m2/d);
        x = k1 * m1 + a1;
        LL m = abs(m1 / d * m2);
        a1 = k1 * m1 + a1;
        m1 = m;
    }
    if (x != -1) x = (a1 % m1 + m1) % m1;
    cout << x << endl;
    return 0;
}

你可能感兴趣的:(算法合集,算法)