中国剩余定理SCL、拓展中国剩余定理SCL

中国剩余定理SCL

【任务】
求出方程组 x ≡ a i ( m o d   m i )   ( 0 ⩽ i &lt; n ) x\equiv a_i(mod\space m_i)\space(0\leqslant i&lt;n) xai(mod mi) (0i<n)的解x。
其中 m 1 , m 2 , m 3 , . . . , m n − 1 m_1,m_2,m_3,...,m_{n-1} m1,m2,m3,...,mn1两两互质

【说明】
M i = ∏ j = i m j M_i=\prod_{j\cancel{=}i}m_j Mi=j= imj。因为 ( M i , m i ) (M_i,m_i) (Mi,mi)=1,故存在 p i , q i p_i,q_i pi,qi,使 M i p i + m i q i = 1 M_ip_i+m_iq_i=1 Mipi+miqi=1
e i = M i p i e_i=M_ip_i ei=Mipi,有:
                               e i ≡ { 0 ( m o d   m j ) , j = i 1 ( m o d   m j ) , j = i \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space \space e_i\equiv \begin{cases}0(mod\space m_j),j\cancel{=}i\\ 1(mod\space m_j),j=i \end{cases}                               ei{0(mod mj),j= i1(mod mj),j=i
e 0 a 0 + e 1 a 1 + e 2 a 2 + . . . + e n − 1 a n − 1 e_0a_0+e_1a_1+e_2a_2+...+e_{n-1}a_{n-1} e0a0+e1a1+e2a2+...+en1an1是方程的一个解。
[ 0 , ∏ i = 0 n − 1 m i ) [0,\prod_{i=0}^{n-1}m_i) [0,i=0n1mi)中只有唯一解,将求出的解对 ∏ i = 0 n − 1 m i \prod_{i=0}^{n-1}m_i i=0n1mi 取模即可。

【接口】
int CRT(int a[],int m[],int n);
复杂度:O(nlogm),其中m和每个 m i m_i mi同阶。
输入:a,m——第i个方程表示为 x ≡ a i ( m o d   m i ) x\equiv a_i(mod\space m_i) xai(mod mi)
           \space\space\space\space\space\space\space\space\space\space           n——方程个数
输出:方程组在 [ 0 , ∏ i = 0 n − 1 m i ) [0,\prod_{i=0}^{n-1}m_i) [0,i=0n1mi)中的解
调用外部函数:拓展欧几里得

【代码】

int CRT( int a[], int m[], int n )//中国剩余定理
{
	int M = 1;
	for(int i=0;i<n;++i) M *= m[i];
	int ret = 0;
	for(int i=0;i<n;++i)
	{
		int x,y;
		int tm = M/m[i];
		extend_gcd(tm,m[i],x,y);//调用外部函数:拓展欧几里得
		ret = (ret+tm*x*a[i])%M;
	}
	return ( (ret+M)%M )?( (ret+M)%M ):M;//特判:如果结果为0,需要输出几个模的最小公倍数
}

拓展中国剩余定理SCL

【任务】
求出方程组 x ≡ a i ( m o d   m i )   ( 0 ⩽ i &lt; n ) x\equiv a_i(mod\space m_i)\space(0\leqslant i&lt;n) xai(mod mi) (0i<n)的解x。
其中 m 1 , m 2 , m 3 , . . . , m n − 1 m_1,m_2,m_3,...,m_{n-1} m1,m2,m3,...,mn1不一定两两互质。

【说明】
见本文下面详细说明。

【接口】
int extend_CRT(int a[],int m[],int n);
复杂度:O(nlogm),其中m和每个 m i m_i mi同阶。
输入:a,m——第i个方程表示为 x ≡ a i ( m o d   m i ) x\equiv a_i(mod\space m_i) xai(mod mi)
           \space\space\space\space\space\space\space\space\space\space           n——方程个数
输出:方程组在 [ 0 , ∏ i = 0 n − 1 m i ) [0,\prod_{i=0}^{n-1}m_i) [0,i=0n1mi)中的解
调用外部函数:拓展欧几里得

【代码】

int extend_CRT( int a[], int m[], int n )//拓展中国剩余定理
{
    int M = m[0];
    int ret = a[0];
    for(int i=1;i<n;++i)
    {
        int x,y;
        int gcd = extend_gcd(M,m[i],x,y);//调用外部函数:拓展欧几里得
        if( (a[i]-ret)%gcd != 0 ) return -1;//无解
        int lcm = M/gcd*m[i];//先除后乘,防爆
        int tm = ((a[i]-ret)/gcd)%(m[i]/gcd);
        ret = (ret+M*x*tm)%lcm;
        M = lcm;
    }
    return ((ret+M)%M)?((ret+M)%M):M;//特判:如果结果为0,需要输出几个模的最小公倍数
}

附:拓展欧几里得SCL

【接口】
int extend_gcd(int a,int b,int &x,int &y);
复杂度:O(logN),其中N和a,b同阶
输入:a,b——两个整数
           \space\space\space\space\space\space\space\space\space\space           &x,&y——引用,ax+by=GCD(a,b)的一组解
输出:a和b的最大公约数
调用后x,y满足方程ax+by=GCD(a,b)

【代码】

int extend_gcd( int a, int b, int &x, int &y )//函数返回a,b的最大公约数
{
	if( b==0 )
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		int r = extend_gcd(b,a%b,y,x);
		y -= x*(a/b);
		return r;
	}
}

以下是中国剩余定理的学习笔记,是转载大神MashiroSky的中国剩余定理学习笔记

中国剩余定理学习笔记

先看一道poj上的题目:【poj1006】 Biorhythms

题意:
  人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这一天,人在对应的方面(体力,情感或智力)表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。

分析:
  首先我们要知道,任意两个峰值之间一定相距整数倍的周期。假设一年的第N天达到峰值,则下次达到峰值的时间为N+Tk(T是周期,k是任意正整数)。所以,三个峰值同时出现的那一天(S)应满足
                                                       S = N 1 + T 1 ∗ k 1 = N 2 + T 2 ∗ k 2 = N 3 + T 3 ∗ k 3 \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space S=N_1+T_1∗k_1=N_2+T_2∗k_2=N_3+T_3∗k_3                                                       S=N1+T1k1=N2+T2k2=N3+T3k3
   N 1 , N 2 , N 3 N_1,N_2,N_3 N1,N2,N3分别为为体力,情感,智力出现峰值的日期, T 1 , T 2 , T 3 T_1,T_2,T_3 T1,T2,T3分别为体力,情感,智力周期。 我们需要求出 k 1 , k 2 , k 3 k_1,k_2,k_3 k1,k2,k3三个非负整数使上面的等式成立。

        \space\space\space\space\space\space\space        想直接求出 k 1 , k 2 , k 3 k_1,k_2,k_3 k1,k2,k3貌似很难,但是我们的目的是求出S, 可以考虑从结果逆推。根据上面的等式,S满足三个要求:除以 T 1 T_1 T1余数为 N 1 N_1 N1,除以 T 2 T_2 T2余数为 N 2 N_2 N2,除以 T 3 T_3 T3余数为 N 3 N_3 N3。这样我们就把问题转化为求一个最小数,该数除以 T 1 T_1 T1 N 1 N_1 N1,除以 T 2 T_2 T2 N 2 N_2 N2,除以 T 3 T_3 T3 N 3 N_3 N3。这就是著名的中国剩余定理,我们的老祖宗在几千年前已经对这个问题想出了一个精妙的解法。依据此解法的算法,时间复杂度可达到 O ( 1 ) O(1) O(1)


中国剩余定理

        \space\space\space\space\space\space\space        在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。具体解法分三步:

  1. 找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
  2. 用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加 15 ∗ 2 + 21 ∗ 3 + 70 ∗ 2 15∗2+21∗3+70∗2 152+213+702得到和233。
  3. 用233除以3,5,7三个数的最小公倍数105,得到余数23,即 233 % 105 = 23 233\%105=23 233%105=23。这个余数23就是符合条件的最小数。

        \space\space\space\space\space\space\space        就这么简单。我们在感叹神奇的同时不禁想知道古人是如何想到这个方法的,有什么基本的数学依据吗?

        \space\space\space\space\space\space\space        我们将“孙子问题”拆分成几个简单的小问题,从零开始,试图揣测古人是如何推导出这个解法的。

        \space\space\space\space\space\space\space        首先,我们假设 n 1 n_1 n1是满足除以3余2的一个数,比如2,5,8等等,也就是满足3∗k+2(k>=0)的一个任意数。同样,我们假设 n 2 n_2 n2是满足除以5余3的一个数, n 3 n_3 n3是满足除以7余2的一个数。

        \space\space\space\space\space\space\space        有了前面的假设,我们先从 n 1 n_1 n1这个角度出发,已知 n 1 n_1 n1满足除以3余2,能不能使得 n 1 + n 2 n_1+n_2 n1+n2的和仍然满足除以3余2?进而使得 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和仍然满足除以3余2?

        \space\space\space\space\space\space\space        这就牵涉到一个最基本数学定理,如果有 a % b = c a\%b=c a%b=c,则有 ( a + k ∗ b ) % b = c ( k 为 非 零 整 数 ) (a+k∗b)\%b=c(k为非零整数) (a+kb)%b=c(k),换句话说,如果一个除法运算的余数为c,那么被除数与 k k k倍的除数相加(或相减)的和(差)再与除数相除,余数不变。这个是很好证明的。

        \space\space\space\space\space\space\space        以此定理为依据,如果 n 2 n_2 n2是3的倍数, n 1 + n 2 n_1+n_2 n1+n2就依然满足除以3余2。同理,如果 n 3 n_3 n3也是3的倍数,那么 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和就满足除以3余2。这是从n1的角度考虑的,再从 n 2 , n 3 n_2,n_3 n2n3的角度出发,我们可推导出以下三点:

  1. 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以3余2, n 2 n_2 n2 n 3 n_3 n3必须是3的倍数。
  2. 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以5余3, n 1 n_1 n1 n 3 n_3 n3必须是5的倍数。
  3. 为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和满足除以7余2, n 1 n_1 n1 n 2 n_2 n2必须是7的倍数。

        \space\space\space\space\space\space\space        因此,为使 n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3的和作为“孙子问题”的一个最终解,需满足:

  1. n 1 n_1 n1除以3余2,且是5和7的公倍数。
  2. n 2 n_2 n2除以5余3,且是3和7的公倍数。
  3. n 3 n_3 n3除以7余2,且是3和5的公倍数。

        \space\space\space\space\space\space\space        所以,孙子问题解法的本质是从5和7的公倍数中找一个除以3余2的数 n 1 n_1 n1,从3和7的公倍数中找一个除以5余3的数 n 2 n_2 n2,从3和5的公倍数中找一个除以7余2的数 n 3 n_3 n3,再将三个数相加得到解。在求 n 1 , n 2 , n 3 n_1,n_2,n_3 n1n2n3时又用了一个小技巧,以 n 1 n_1 n1为例,并非从5和7的公倍数中直接找一个除以3余2的数,而是先找一个除以3余1的数,再乘以2。也就是先求出5和7的公倍数模3下的逆元,再用逆元去乘余数。

        \space\space\space\space\space\space\space        这里又有一个数学公式,如果 a % b = c a\%b=c a%b=c,那么 ( a ∗ k ) % b = a % b + a % b + … + a % b = c + c + … + c = k ∗ c ( k &gt; 0 ) (a∗k)\%b=a\%b+a\%b+…+a\%b=c+c+…+c=k∗c(k&gt;0) (ak)%b=a%b+a%b++a%b=c+c++c=kc(k>0),也就是说,如果一个除法的余数为 c c c,那么被除数的 k k k倍与除数相除的余数为 k ∗ c k∗c kc。展开式中已证明。

        \space\space\space\space\space\space\space        最后,我们还要清楚一点, n 1 + n 2 + n 3 n_1+n_2+n_3 n1+n2+n3只是问题的一个解,并不是最小的解。如何得到最小解?我们只需要从中最大限度的减掉掉3,5,7的公倍数105即可。道理就是前面讲过的定理“如果 a % b = c a\%b=c a%b=c,则有 ( a − k ∗ b ) % b = c (a−k∗b)\%b=c (akb)%b=c”。所以 ( n 1 + n 2 + n 3 ) % 105 (n_1+n_2+n_3)\%105 n1+n2+n3%105 就是最终的最小解。

        \space\space\space\space\space\space\space        这样一来就得到了中国剩余定理的公式:

设正整数 m 1 , m 2 , . . . , m k m_1,m_2,...,m_k m1,m2,...,mk两两互素,则同余方程组
                                           { 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 k ( m o d   m k ) \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space \begin{cases} x\equiv a_1(mod\space m_1)\\ x\equiv a_2(mod\space m_2)\\ x\equiv a_3(mod\space m_3)\\ \space\space\space\space .\\ \space\space\space\space .\\ \space\space\space\space .\\ x\equiv a_k(mod\space m_k)\end{cases}                                           xa1(mod m1)xa2(mod m2)xa3(mod m3)    .    .    .xak(mod mk)

有整数解。并且在模 M = m 1 ⋅ m 2 ⋅ . . . ⋅ m k M=m_1\cdot m_2\cdot...\cdot m_k M=m1m2...mk下的解是唯一的,解为
                                           x ≡ ( a 1 M 1 M 1 − 1 + a 2 M 2 M 2 − 1 + . . . + a k M k M k − 1 ) m o d   M \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x\equiv(a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+...+a_kM_kM_k^{-1})mod\space M                                           x(a1M1M11+a2M2M21+...+akMkMk1)mod M

其中 M i = M / m i M_i=M/m_i Mi=M/mi ,而 M i − 1 M_i^{-1} Mi1 M i M_i Mi m i m_i mi 的逆元。


中国剩余定理扩展——求解模数不互质情况下的线性方程组:

        \space\space\space\space\space\space\space        普通的中国剩余定理要求所有的 m i m_i mi互素,那么如果不互素呢,怎么求解同余方程组?

        \space\space\space\space\space\space\space        这种情况就采用两两合并的思想,假设要合并如下两个方程:

                                           x = a 1 + m 1 x 1                                            x = a 2 + m 2 x 2 \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x=a_1+m_1x_1\\\space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space x=a_2+m_2x_2                                           x=a1+m1x1                                          x=a2+m2x2

        \space\space\space\space\space\space\space        那么得到:
                           a 1 + m 1 x 1 = a 2 + m 2 x 2 ⇒ m 1 x 1 + m 2 x 2 = a 2 − a 1 \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space a_1+m_1x_1=a_2+m_2x_2\Rightarrow m_1x_1+m_2x_2=a_2-a_1                        a1+m1x1=a2+m2x2m1x1+m2x2=a2a1

        \space\space\space\space\space\space\space        我们需要求出一个最小的x使它满足:
                               x = a 1 + m 1 x 1 = a 2 + m 2 x 2 \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space x=a_1+m_1x_1=a_2+m_2x_2                               x=a1+m1x1=a2+m2x2

        \space\space\space\space\space\space\space        那么 x 1 x_1 x1 x 2 x_2 x2就要尽可能的小,于是我们用扩展欧几里得算法求出 x 1 x_1 x1的最小正整数解,将它代回 a 1 + m 1 x 1 a_1+m_1x_1 a1+m1x1,得到x的一个特解 x ′ x&#x27; x ,当然也是最小正整数解。

        \space\space\space\space\space\space\space        所以 x 的通解一定是 x ′ x&#x27; x 加上 l c m ( m 1 , m 2 ) ∗ k lcm(m_1,m_2)∗k lcm(m1,m2)k,这样才能保证x模 m 1 m_1 m1 m 2 m_2 m2的余数是 a 1 a_1 a1 a 2 a_2 a2。由此,我们把这个 x ′ x&#x27; x 当做新的方程的余数,把 l c m ( m 1 , m 2 ) lcm(m_1,m_2) lcm(m1,m2)当做新的方程的模数。(这一段是关键

        \space\space\space\space\space\space\space        合并完成:
                               x ≡ x ′   ( m o d   l c m ( m 1 , m 2 )   ) \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space \space\space\space\space\space\space\space\space\space\space x\equiv x&#x27;\space(mod\space lcm(m_1,m_2)\space)                               xx (mod lcm(m1,m2) )


总结

        \space\space\space\space\space\space\space        经过分析发现,中国剩余定理的孙子解法并没有什么高深的技巧,就是以下两个基本数学定理的灵活运用:

  1. 如果 a%b=c , 则有 (a+kb)%b=c (k为非零整数)。
  2. 如果 a%b=c,那么 (a*k)%b=kc (k为大于零的整数)。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值