中国剩余定理

前言

如果你正在看这篇博客,我就默认你会算乘法逆元了,如果你还不会,请看我的早期博客:乘法逆元

定义

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?——《孙子算经》

即求满足以下条件的整数:除以3余2,除以5余3,除以7余2。

宋朝数学家秦九韶于 1247 年《数书九章》卷一、二《大衍类》对「物不知数」问题做出了完整系统的解答。上面具体问题的解答口诀由明朝数学家程大位在《算法统宗》中给出:

三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知。

\[2 \times 70 + 3 \times 21 + 2 \times 15 = 233 = 2 \times 105 + 23 \]

,故答案为23。

我们管这种解决一元余数方程组的最小整数解的方法叫做中国剩余定理

中国剩余定理的基本式为:

\[\begin{cases}x \equiv a_1 \pmod{ m_1 }\\x \equiv a_2 \pmod{ m_2 }\\x \equiv a_2 \pmod{ m_3 } \\ …… \\ x \equiv a_n \pmod{ m_n } \end{cases} \]

算法和正确性证明

我们可以从拆分子问题来试图理解一下古人精妙的思路

假设我们要求3个数

\[n_1 , n_2 , n_3 \]

,它们分别满足

\[\begin{cases}n_1 \equiv 2 \pmod{ 3 }\\n_2 \equiv 3 \pmod{ 5 }\\n_3 \equiv 2 \pmod{ 7 } \end{cases} \]

而且,我们分别让另外两个数是该式子模数的倍数

因为我们知道

\[a \mod b = c \]

\[( a + k \times b ) \mod b = c \]

,所以

\[\begin{cases}( n_1 + n_2 + n_3 ) \mod 3 = 2 \\( n_1 + n_2 + n_3 ) \mod 5 = 3 \\ ( n_1 + n_2 + n_3 ) \mod 7 = 2 \\ \end{cases}\]

我们就可以求解这个方程组,将

\[n_1,n_2,n_3 \]

分别求解,最后的答案就是

\[n_1 + n_2 + n_3 \]

我们就可以从5和7的倍数中,找一个余数为2的数即可求出

\[n_1 \]

我们都知道

\[a \mod b = c \]

\[k \times a \mod b = k \times c \]

,所以我们在求解

\[n_1 \mod 3 = 2 \]

时,我们可以用到一个小技巧,我们可以先求出在mod 3的意义下的逆元

\[n_1^{-1} \]

,再乘上该式子的余数2,即可求出一个解

注意:在这里的运算中,我们只是求出符合题意的一个解,最后,我们只需

\[\mod 3 \times 5 \times 7 \]

具体实践的注意事项

  1. 在中国剩余定理的运算中,我们默认每个模数都两两互质,若不互质,则需要另一个优化手段,在此不加以赘述,想要进阶的同学请看参考文献中的中国剩余定理学习笔记

  2. 文章中的乘法逆元若不说明统一指乘法逆元中的最小正整数解

算法实现与练习

每日一板:P1495 【模板】中国剩余定理(CRT)/曹冲养猪

上代码:

点击查看代码
#include<iostream>
using namespace std;
typedef long long ll;
ll x,y;
void exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	ll tx=x;
	x=y;
	y=tx-a/b*y;
}
int n;
ll a[11],b[11];
ll A=1;
ll ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i]>>b[i],
		A*=a[i];
	for(int i=1;i<=n;i++)
	{
		x=y=0;
		ll nw=A/a[i];
		exgcd(nw,a[i]);
		x=(x%a[i]+a[i])%a[i];
		ans=(ans%A+nw*x*b[i]%A)%A;
	}
	cout<<ans<<endl;
	return 0;
}

本文参考文献:oi-wiki中国剩余定理学习笔记

每篇撒花

上一篇:算法-链表:链表的常见六个操作


下一篇:[CF1613D]MEX sequences 题解