「物不知数」问题:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
即求满足以下条件的整数:除以 3 3 3 余 2 2 2 ,除以 5 5 5 余 3 3 3 ,除以 7 7 7 余 2 2 2 。
该问题最早见于《孙子算经》中,并有该问题的具体解法。宋朝数学家秦九韶于 1247 年《数书九章》卷一、二《大衍类》对「物不知数」问题做出了完整系统的解答。上面具体问题的解答口诀由明朝数学家程大位在《算法统宗》中给出:
三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知。
2 × 70 + 3 × 21 + 2 × 15 = 233 = 2 × 105 + 23 2\times 70+3\times 21+2\times 15=233=2\times 105+23 2 × 70 + 3 × 21 + 2 × 15 = 233 = 2 × 105 + 23 ,故答案为 23 23 23 。
中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中 n 1 , n 2 , ⋯ , n k n_1, n_2, \cdots, n_k n 1 , n 2 , ⋯ , n k 两两互质):
{ x ≡ a 1 ( m o d n 1 ) x ≡ a 2 ( m o d n 2 ) ⋮ x ≡ a k ( m o d n k ) \begin{cases}
x &\equiv a_1 \pmod {n_1} \\
x &\equiv a_2 \pmod {n_2} \\
&\vdots \\
x &\equiv a_k \pmod {n_k} \\
\end{cases} ⎩ ⎨ ⎧ x x x ≡ a 1 ( mod n 1 ) ≡ a 2 ( mod n 2 ) ⋮ ≡ a k ( mod n k )
上面的「物不知数」问题就是一元线性同余方程组的一个实例。
计算所有模数的积 n n n ;
对于第 i i i 个方程:
计算 m i = n n i m_i=\frac{n}{n_i} m i = n i n ;
计算 m i m_i m i 在模 n i n_i n i 意义下的乘法逆元 m i − 1 m_i^{-1} m i − 1 ;
计算 c i = m i m i − 1 c_i=m_im_i^{-1} c i = m i m i − 1 (不要对 n i n_i n i 取模 )。
方程组在模 n n n 意义下的唯一解为:x = ∑ i = 1 k a i c i ( m o d n ) x=\sum_{i=1}^k a_ic_i \pmod n x = ∑ i = 1 k a i c i ( mod n ) 。
LL CRT ( int k , LL * a , LL * r ) { LL n = 1 , ans = 0 ; for ( int i = 1 ; i <= k ; i ++ ) n = n * r [ i ] ; for ( int i = 1 ; i <= k ; i ++ ) { LL m = n / r [ i ] , b , y ; exgcd ( m , r [ i ] , b , y ) ; ans = ( ans + a [ i ] * m * b % n ) % n ; } return ( ans % n + n ) % n ; }
我们需要证明上面算法计算所得的 x x x 对于任意 i = 1 , 2 , ⋯ , k i=1,2,\cdots,k i = 1 , 2 , ⋯ , k 满足 x ≡ a i ( m o d n i ) x\equiv a_i \pmod {n_i} x ≡ a i ( mod n i ) 。
当 i ≠ j i\neq j i = j 时,有 m j ≡ 0 ( m o d n i ) m_j \equiv 0 \pmod {n_i} m j ≡ 0 ( mod n i ) ,故 c j ≡ m j ≡ 0 ( m o d n i ) c_j \equiv m_j \equiv 0 \pmod {n_i} c j ≡ m j ≡ 0 ( mod n i ) 。又有 c i ≡ m i ⋅ ( m i − 1 m o d n i ) ≡ 1 ( m o d n i ) c_i \equiv m_i \cdot (m_i^{-1} \bmod {n_i}) \equiv 1 \pmod {n_i} c i ≡ m i ⋅ ( m i − 1 mod n i ) ≡ 1 ( mod n i ) ,所以我们有:
x ≡ ∑ j = 1 k a j c j ( m o d n i ) ≡ a i c i ( m o d n i ) ≡ a i ⋅ m i ⋅ ( m i − 1 m o d n i ) ( m o d n i ) ≡ a i ( m o d n i ) \begin{aligned}
x&\equiv \sum_{j=1}^k a_jc_j &\pmod {n_i} \\
&\equiv a_ic_i &\pmod {n_i} \\
&\equiv a_i \cdot m_i \cdot (m^{-1}_i \bmod n_i) &\pmod {n_i} \\
&\equiv a_i &\pmod {n_i}
\end{aligned} x ≡ j = 1 ∑ k a j c j ≡ a i c i ≡ a i ⋅ m i ⋅ ( m i − 1 mod n i ) ≡ a i ( mod n i ) ( mod n i ) ( mod n i ) ( mod n i )
即对于任意 i = 1 , 2 , ⋯ , k i=1,2,\cdots,k i = 1 , 2 , ⋯ , k ,上面算法得到的 x x x 总是满足 x ≡ a i ( m o d n i ) x\equiv a_i \pmod{n_i} x ≡ a i ( mod n i ) ,即证明了解同余方程组的算法的正确性。
因为我们没有对输入的 a i a_i a i 作特殊限制,所以任何一组输入 { a i } \{a_i\} { a i } 都对应一个解 x x x 。
另外,若 x ≠ y x\neq y x = y ,则总存在 i i i 使得 x x x 和 y y y 在模 n i n_i n i 下不同余。
故系数列表 { a i } \{a_i\} { a i } 与解 x x x 之间是一一映射关系,方程组总是有唯一解。
下面演示 CRT 如何解「物不知数」问题。
n = 3 × 5 × 7 = 105 n=3\times 5\times 7=105 n = 3 × 5 × 7 = 105 ;
三人同行 七十 希:n 1 = 3 , m 1 = n / n 1 = 35 , m 1 − 1 ≡ 2 ( m o d 3 ) n_1=3, m_1=n/n_1=35, m_1^{-1}\equiv 2\pmod 3 n 1 = 3 , m 1 = n / n 1 = 35 , m 1 − 1 ≡ 2 ( mod 3 ) ,故 c 1 = 35 × 2 = 70 c_1=35\times 2=70 c 1 = 35 × 2 = 70 ;
五树梅花 廿一 支:n 2 = 5 , m 2 = n / n 2 = 21 , m 2 − 1 ≡ 1 ( m o d 5 ) n_2=5, m_2=n/n_2=21, m_2^{-1}\equiv 1\pmod 5 n 2 = 5 , m 2 = n / n 2 = 21 , m 2 − 1 ≡ 1 ( mod 5 ) ,故 c 2 = 21 × 1 = 21 c_2=21\times 1=21 c 2 = 21 × 1 = 21 ;
七子团圆正 半月 :n 3 = 7 , m 3 = n / n 3 = 15 , m 3 − 1 ≡ 1 ( m o d 7 ) n_3=7, m_3=n/n_3=15, m_3^{-1}\equiv 1\pmod 7 n 3 = 7 , m 3 = n / n 3 = 15 , m 3 − 1 ≡ 1 ( mod 7 ) ,故 c 3 = 15 × 1 = 15 c_3=15\times 1=15 c 3 = 15 × 1 = 15 ;
所以方程组的唯一解为 x ≡ 2 × 70 + 3 × 21 + 2 × 15 ≡ 233 ≡ 23 ( m o d 105 ) x\equiv 2\times 70+3\times 21+2\times 15\equiv 233\equiv 23 \pmod {105} x ≡ 2 × 70 + 3 × 21 + 2 × 15 ≡ 233 ≡ 23 ( mod 105 ) 。(除 百零五 便得知)
Garner 算法
CRT 的另一个用途是用一组比较小的质数表示一个大的整数。
例如,若 a a a 满足如下线性方程组,且 a < ∏ i = 1 k p i a < \prod_{i=1}^k p_i a < ∏ i = 1 k p i (其中 p i p_i p i 为质数):
{ a ≡ a 1 ( m o d p 1 ) a ≡ a 2 ( m o d p 2 ) ⋮ a ≡ a k ( m o d p k ) \begin{cases}
a &\equiv a_1 \pmod {p_1} \\
a &\equiv a_2 \pmod {p_2} \\
&\vdots \\
a &\equiv a_k \pmod {p_k} \\
\end{cases} ⎩ ⎨ ⎧ a a a ≡ a 1 ( mod p 1 ) ≡ a 2 ( mod p 2 ) ⋮ ≡ a k ( mod p k )
我们可以用以下形式的式子(称作 a a a 的混合基数表示)表示 a a a :
a = x 1 + x 2 p 1 + x 3 p 1 p 2 + … + x k p 1 … p k − 1 a = x_1 + x_2 p_1 + x_3 p_1 p_2 + \ldots + x_k p_1 \ldots p_{k-1} a = x 1 + x 2 p 1 + x 3 p 1 p 2 + … + x k p 1 … p k − 1
Garner 算法 将用来计算系数 x 1 , … , x k x_1, \ldots, x_k x 1 , … , x k 。
令 r i j r_{ij} r ij 为 p i p_i p i 在模 p j p_j p j 意义下的乘法逆元:
p i ⋅ r i , j ≡ 1 ( m o d p j ) p_i \cdot r_{i,j} \equiv 1 \pmod{p_j} p i ⋅ r i , j ≡ 1 ( mod p j )
把 a a a 代入我们得到的第一个方程:
a 1 ≡ x 1 ( m o d p 1 ) a_1 \equiv x_1 \pmod{p_1} a 1 ≡ x 1 ( mod p 1 )
代入第二个方程得出:
a 2 ≡ x 1 + x 2 p 1 ( m o d p 2 ) a_2 \equiv x_1 + x_2 p_1 \pmod{p_2} a 2 ≡ x 1 + x 2 p 1 ( mod p 2 )
方程两边减 x 1 x_1 x 1 ,除 p 1 p_1 p 1 后得
a 2 − x 1 ≡ x 2 p 1 ( m o d p 2 ) ( a 2 − x 1 ) r 1 , 2 ≡ x 2 ( m o d p 2 ) x 2 ≡ ( a 2 − x 1 ) r 1 , 2 ( m o d p 2 ) \begin{aligned}
a_2 - x_1 &\equiv x_2 p_1 &\pmod{p_2} \\
(a_2 - x_1) r_{1,2} &\equiv x_2 &\pmod{p_2} \\
x_2 &\equiv (a_2 - x_1) r_{1,2} &\pmod{p_2}
\end{aligned} a 2 − x 1 ( a 2 − x 1 ) r 1 , 2 x 2 ≡ x 2 p 1 ≡ x 2 ≡ ( a 2 − x 1 ) r 1 , 2 ( mod p 2 ) ( mod p 2 ) ( mod p 2 )
类似地,我们可以得到:
x k = ( … ( ( a k − x 1 ) r 1 , k − x 2 ) r 2 , k ) − … ) r k − 1 , k m o d p k x_k=(\dots((a_k-x_1)r_{1,k}-x_2)r_{2,k})-\dots)r_{k-1,k} \bmod p_k x k = ( … (( a k − x 1 ) r 1 , k − x 2 ) r 2 , k ) − … ) r k − 1 , k mod p k
for ( int i = 0 ; i < k ; ++ i ) { x [ i ] = a [ i ] ; for ( int j = 0 ; j < i ; ++ j ) { x [ i ] = r [ j ] [ i ] * ( x [ i ] - x [ j ] ) ; x [ i ] = x [ i ] % p [ i ] ; if ( x [ i ] < 0 ) x [ i ] += p [ i ] ; } }
该算法的时间复杂度为 O ( k 2 ) O(k^2) O ( k 2 ) 。实际上 Garner 算法并不要求模数为质数,只要求模数两两互质,我们有如下伪代码:
Chinese Remainder Algorithm cra ( v , m ) : Input : m = ( m 0 , m 1 , … , m n − 1 ) , m i ∈ Z + ∧ gcd ( m i , m j ) = 1 for all i ≠ j , v = ( v 0 , … , v n − 1 ) where v i = x m o d m i . Output : x m o d ∏ i = 0 n − 1 m i . 1 for i from 1 to ( n − 1 ) do 2 C i ← ( ∏ j = 0 i − 1 m j ) − 1 m o d m i 3 x ← v 0 4 for i from 1 to ( n − 1 ) do 5 u ← ( v i − x ) ⋅ C i m o d m i 6 x ← x + u ∏ j = 0 i − 1 m j 7 return ( x ) \begin{array}{ll}
&\textbf{Chinese Remainder Algorithm }\operatorname{cra}(\mathbf{v}, \mathbf{m})\text{:} \\
&\textbf{Input}\text{: }\mathbf{m}=(m_0,m_1,\dots ,m_{n-1})\text{, }m_i\in\mathbb{Z}^+\land\gcd(m_i,m_j)=1\text{ for all } i\neq j\text{,} \\
&\qquad \mathbf{v}=(v_0,\dots ,v_{n-1}) \text{ where }v_i=x\bmod m_i\text{.} \\
&\textbf{Output}\text{: }x\bmod{\prod_{i=0}^{n-1} m_i}\text{.} \\
1&\qquad \textbf{for }i\text{ from }1\text{ to }(n-1)\textbf{ do} \\
2&\qquad \qquad C_i\gets \left(\prod_{j=0}^{i-1}m_j\right)^{-1}\bmod{m_i} \\
3&\qquad x\gets v_0 \\
4&\qquad \textbf{for }i\text{ from }1\text{ to }(n-1)\textbf{ do} \\
5&\qquad \qquad u\gets (v_i-x)\cdot C_i\bmod{m_i} \\
6&\qquad \qquad x\gets x+u\prod_{j=0}^{i-1}m_j \\
7&\qquad \textbf{return }(x)
\end{array} 1 2 3 4 5 6 7 Chinese Remainder Algorithm cra ( v , m ) : Input : m = ( m 0 , m 1 , … , m n − 1 ) , m i ∈ Z + ∧ g cd( m i , m j ) = 1 for all i = j , v = ( v 0 , … , v n − 1 ) where v i = x mod m i . Output : x mod ∏ i = 0 n − 1 m i . for i from 1 to ( n − 1 ) do C i ← ( ∏ j = 0 i − 1 m j ) − 1 mod m i x ← v 0 for i from 1 to ( n − 1 ) do u ← ( v i − x ) ⋅ C i mod m i x ← x + u ∏ j = 0 i − 1 m j return ( x )
可以发现在第六行中的计算过程对应上述混合基数的表示。
某些计数问题或数论问题出于加长代码、增加难度、或者是一些其他原因,给出的模数:不是质数 !
但是对其质因数分解会发现它没有平方因子,也就是该模数是由一些不重复的质数相乘得到。
那么我们可以分别对这些模数进行计算,最后用 CRT 合并答案。
下面这道题就是一个不错的例子。
???+ note "洛谷 P2480 [SDOI2010] 古代猪文 "
给出 G , n G,n G , n (1 ≤ G , n ≤ 10 9 1 \leq G,n \leq 10^9 1 ≤ G , n ≤ 1 0 9 ),求:
G ∑ k ∣ n ( n k ) m o d 999 911 659 G^{\sum_{k\mid n}\binom{n}{k}} \bmod 999~911~659 G ∑ k ∣ n ( k n ) mod 999 911 659
首先,当 G = 999 911 659 G=999~911~659 G = 999 911 659 时,所求显然为 0 0 0 。
否则,根据欧拉定理,可知所求为:
G ∑ k ∣ n ( n k ) m o d 999 911 658 m o d 999 911 659 G^{\sum_{k\mid n}\binom{n}{k} \bmod 999~911~658} \bmod 999~911~659 G ∑ k ∣ n ( k n ) mod 999 911 658 mod 999 911 659
现在考虑如何计算:
∑ k ∣ n ( n k ) m o d 999 911 658 \sum_{k\mid n}\binom{n}{k} \bmod 999~911~658 k ∣ n ∑ ( k n ) mod 999 911 658
因为 999 911 658 999~911~658 999 911 658 不是质数,无法保证 ∀ x ∈ [ 1 , 999 911 657 ] \forall x \in [1,999~911~657] ∀ x ∈ [ 1 , 999 911 657 ] ,x x x 都有逆元存在,上面这个式子我们无法直接计算。
注意到 999 911 658 = 2 × 3 × 4679 × 35617 999~911~658=2 \times 3 \times 4679 \times 35617 999 911 658 = 2 × 3 × 4679 × 35617 ,其中每个质因子的最高次数均为一,我们可以考虑分别求出 ∑ k ∣ n ( n k ) \sum_{k\mid n}\binom{n}{k} ∑ k ∣ n ( k n ) 在模 2 2 2 ,3 3 3 ,4679 4679 4679 ,35617 35617 35617 这几个质数下的结果,最后用中国剩余定理来合并答案。
也就是说,我们实际上要求下面一个线性方程组的解:
{ x ≡ a 1 ( m o d 2 ) x ≡ a 2 ( m o d 3 ) x ≡ a 3 ( m o d 4679 ) x ≡ a 4 ( m o d 35617 ) \begin{cases}
x \equiv a_1 \pmod 2\\
x \equiv a_2 \pmod 3\\
x \equiv a_3 \pmod {4679}\\
x \equiv a_4 \pmod {35617}
\end{cases} ⎩ ⎨ ⎧ x ≡ a 1 ( mod 2 ) x ≡ a 2 ( mod 3 ) x ≡ a 3 ( mod 4679 ) x ≡ a 4 ( mod 35617 )
而计算一个组合数对较小的质数取模后的结果,可以利用卢卡斯定理。
扩展:模数不互质的情况
两个方程
设两个方程分别是 x ≡ a 1 ( m o d m 1 ) x\equiv a_1 \pmod {m_1} x ≡ a 1 ( mod m 1 ) 、x ≡ a 2 ( m o d m 2 ) x\equiv a_2 \pmod {m_2} x ≡ a 2 ( mod m 2 ) ;
将它们转化为不定方程:x = m 1 p + a 1 = m 2 q + a 2 x=m_1p+a_1=m_2q+a_2 x = m 1 p + a 1 = m 2 q + a 2 ,其中 p , q p, q p , q 是整数,则有 m 1 p − m 2 q = a 2 − a 1 m_1p-m_2q=a_2-a_1 m 1 p − m 2 q = a 2 − a 1 。
由裴蜀定理,当 a 2 − a 1 a_2-a_1 a 2 − a 1 不能被 gcd ( m 1 , m 2 ) \gcd(m_1,m_2) g cd( m 1 , m 2 ) 整除时,无解;
其他情况下,可以通过扩展欧几里得算法解出来一组可行解 ( p , q ) (p, q) ( p , q ) ;
则原来的两方程组成的模方程组的解为 x ≡ b ( m o d M ) x\equiv b\pmod M x ≡ b ( mod M ) ,其中 b = m 1 p + a 1 b=m_1p+a_1 b = m 1 p + a 1 ,M = lcm ( m 1 , m 2 ) M=\text{lcm}(m_1, m_2) M = lcm ( m 1 , m 2 ) 。
多个方程
用上面的方法两两合并即可。