【数学】一元一次同余方程组、中国剩余定理(CRT)与扩展中国剩余定理(exCRT)

一元一次同余方程组

形如 { x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 )                 ⋮ x ≡ a n ( m o d m n ) \begin{cases}x\equiv a_1\pmod{m_1}\\x\equiv a_2\pmod{m_2}\\\>\>\>\>\>\>\>\>\>\>\>\>\>\>\>\vdots\\x\equiv a_n\pmod{m_n}\end{cases} xa1(modm1)xa2(modm2)xan(modmn) 的方程组称作一元一次同余方程组。

中国剩余定理(CRT)

中国剩余定理(以下简称 CRT)对应的是该类方程组满足 gcd ⁡ i = 1 n m i = 1 \gcd\limits^n_{i=1}m_i=1 i=1gcdnmi=1的求解方法。
早在《孙子算经》中就提出了一元一次同余方程租的基本解法,在秦九韶提出“大衍求一术”后才构成了我们所知的 CRT。
CRT是这么算的:
首先设 M = ∏ i = 1 n m i M=\prod\limits^n_{i=1}m_i M=i=1nmi M i = M m i M_i=\frac M{m_i} Mi=miM
再设 x i x_i xi 满足方程组 { x i ≡ 0 ( m o d m j ) if  j ≠ i x i ≡ 1 ( m o d m j ) if  j = i \begin{cases}x_i\equiv 0\pmod{m_j}&\text{if }j\ne i\\x_i\equiv 1\pmod{m_j}&\text{if }j=i \end{cases} {xi0(modmj)xi1(modmj)if j=iif j=i
不难解得 x i ≡ M i M i − 1 ( m o d M ) x_i\equiv M_iM_i^{-1}\pmod{M} xiMiMi1(modM) 其中逆元为模 m i m_i mi 下的。
逆元不妨用扩展欧几里得算法获得,也就是秦九韶的“大衍求一术”。
最后得到 x ≡ ∑ i = 1 n x i ≡ ∑ i = 1 n M i M i − 1 ( m o d M ) x\equiv\sum\limits^n_{i=1}x_i\equiv\sum\limits^n_{i=1}M_iM_i^{-1}\pmod{M} xi=1nxii=1nMiMi1(modM),即中国剩余定理。

扩展中国剩余定理(exCRT)

扩展中国剩余定理(以下简称 exCRT)去掉了 CRT 中的要求,可以对一元一次同余方程组直接求解。
但是 exCRT 相比之下显得非常暴力,简单来说就是使用扩展欧几里得算法暴力合并方程。
比如合并方程 { x ≡ a i ( m o d m i ) x ≡ a j ( m o d m j ) \begin{cases}x\equiv a_i\pmod{m_i}\\x\equiv a_j\pmod{m_j}\end{cases} {xai(modmi)xaj(modmj)
我们设 k i ∈ Z k_i\in\Z kiZ,满足 a i + k i m i ≡ a j ( m o d m j ) a_i+k_im_i\equiv a_j\pmod{m_j} ai+kimiaj(modmj)
再设 k j ∈ Z k_j\in\Z kjZ,整理得 k i m i + k j m j = a j − a i k_im_i+k_jm_j=a_j-a_i kimi+kjmj=ajai,可以借助扩展欧几里得算法求出 k i , k j k_i,k_j ki,kj
则得到的解显然是 x ≡ k i ⋅ a j − a i gcd ⁡ ( m i , m j ) ( m o d m i m j gcd ⁡ ( m i , m j ) ) x\equiv k_i\cdot\frac{a_j-a_i}{\gcd(m_i,m_j)}\pmod{\frac{m_im_j}{\gcd(m_i,m_j)}} xkigcd(mi,mj)ajai(modgcd(mi,mj)mimj)
在这个条件下,有可能会无解。当且仅当两个方程中 a i ≢ a j ( m o d gcd ⁡ ( m i , m j ) ) a_i\not\equiv a_j\pmod{\gcd(m_i,m_j)} aiaj(modgcd(mi,mj)) 会出现矛盾,导致无解,注意特判。

代码

  • CRT
void exgcd(int a,int b,int &x,int &y){
    if(b==0){x=1;y=0;return;}
    exgcd(b,a%b,x,y);
    int z=x;x=y,y=z-y*(a/b);
}
int CRT(int n,int a[],int m[]){
    cin>>n;
    for (int i=1;i<=n;i++){
        int k;cin>>k;m[i]=k;
        p*=k;cin>>a[i];
    }
    for (int i=1;i<=n;i++){
        t[i]=p/m[i];
        int u=0,v=0;
        exgcd(t[i],m[i],u,v);
        if (u<0) x+=a[i]*t[i]*(u+m[i]);
        else x+=a[i]*t[i]*u;
    }
    return x%p;
}
  • exCRT
int exgcd(int a,int b,int &x,int &y){
	if (!b){x=1;y=0;return a;}
	int d=exgcd(b,a%b,y,x);
	y-=a/b*x;
    return d;
}
int exCRT(int n,int a[],int m[]){
	x=0,y=0,c=a[1],d=m[1];
	for (int i=2;i<=n;i++){
		int g=exgcd(d,m[i],x,y);
		if (c%g!=a[i]%g) return -1;
		x=(a[i]-c)/g*x%(m[i]/g);
		c+=d*x;
		d=d/g*m[i];
		c%=d;
	}
	if ((c+d)%d) return (c+d)%d;
	else return d;
}

练习

  • 洛谷P1495
  • 洛谷P4777

你可能感兴趣的:(数学,#,数论,c++,OI,数学,算法,数论)