中国剩余定理(CRT)与 EXCRT 学习笔记
前言
中国剩余定理,是中国古代的一个典型的数学问题。它用于解决 (同一个数)(模多个值)(出现多个不同余数) 的问题。典型代表是韩信点兵。
看到网上的博客模模糊糊的,本蒟蒻决定按照自己的理解梳理一下这俩玩意(毕竟它们实在是太重要了),同时也方便后来人学习。
本篇博客将分为两部分:传统的 CRT 和扩展 CRT。
Part 1:中国剩余定理(CRT)
假设有以下
且对于任意一组
缩放问题
我们先讨论特殊情况:
今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?
意思是:现在有一堆物品,
3 个为一组剩2 个,5 个为一组剩3 个,7 个为一组剩2 个。问这堆物品(最少)有几个?
答案是 废话,肯定不是枚举)?
- 求出三个数:
lcm(3,5)=15,lcm(3,7)=21,lcm(5,7)=35 。 - 将
15\times 2,21\times 3,35\times 2 ,即分别乘上另外一个数除以该数的余数。 - 相加,得
30+63+140=233 。233 符合条件,但不是最小,模上lcm(3,5,7)=105 得23 ,所以答案即为23 。
看似很牛 X。实际上,这些步骤是有严格数学证明的。
下面,我们一步一步看(这是最关键的地方,其他博客就是这里讲得一塌糊涂)。
原理及推导过程
首先需要明确同余的两个性质:
- 若
a\equiv b\pmod m ,则a+km\equiv b\pmod m (k 为任意非零整数)。 - 若
a\equiv b\pmod m ,则ak\equiv bk\pmod m 。
我们假设有三个未知数
现在,我们稍微合并一下三个方程:
- 若
p_1+p_2\equiv 2\pmod 3 ,则根据性质1 ,3\mid p_2 。 - 若
p_2+p_3\equiv 3\pmod 5 ,则根据性质1 ,5\mid p_3 。 - 若
p_3+p_1\equiv 2\pmod 7 ,则根据性质1 ,7\mid p_1 。
现在,我们考虑构造答案:
- 若
p_1+p_2+p_3\equiv 2\pmod 3 ,则根据性质1 ,3\mid p_3 。 - 若
p_1+p_2+p_3\equiv 3\pmod 5 ,则根据性质1 ,5\mid p_1 。 - 若
p_1+p_2+p_3\equiv 2\pmod 7 ,则根据性质1 ,7\mid p_2 。
整理一下,现在我们得到一些关于
如果还是不懂,我转化一下,即:
所以,我们就要从
这该咋找呢?我们想到了扩展欧几里得。
我们以求
为什么不直接求出
所以,当我们求出方程①的解后,
剩下的
得到了
为什么模上
我们知道,模上一个数等于减去若干个这个数。而由性质
最后给一首诗帮助记忆:三人同行七十稀,五树梅花廿一枝, 七子团圆月正半,除百零五便得知。
至此,我们已经解决了三个方程的中国剩余定理。接下来,我们就扩展一下,看看
扩大问题
现在升级一波:有
设有
现在,我们稍微合并一下
- 若
p_1+p_2\equiv a_1\pmod {m_1} ,则根据性质1 ,m_1\mid p_2 。 - 若
p_2+p_3\equiv a_2\pmod {m_2} ,则根据性质1 ,m_2\mid p_3 。 - 若
p_3+p_4\equiv a_3\pmod {m_3} ,则根据性质1 ,m_3\mid p_4 。 -
\cdots - 若
p_n+p_1\equiv a_n\pmod {m_n} ,则根据性质1 ,m_n\mid p_1 。
再扩展成
- 若
p_1+p_2+p_3\equiv a_1\pmod {m_1} ,则根据性质1 ,m_1\mid p_3 。 - 若
p_2+p_3+p_4\equiv a_2\pmod {m_2} ,则根据性质1 ,m_2\mid p_4 。 -
\cdots - 若
p_n+p_1+p_2\equiv a_n\pmod {m_n} ,则根据性质1 ,m_n\mid p_2 。
以此类推,直到出现
整理一下,现在我们得到一些关于
-
-
-
-
\cdots -
这时发现,令
然后再根据求三个数时候的方法对每一个
因为互质,所以
最后分析一下复杂度:枚举 + 扩欧,时间复杂度
代码
模板题:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪 是真的模板。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 10 + 10;
int n, lcm = 1, ans, a[N], m[N];//m[i] 模数,a[i] 余数
void exgcd(int a, int b, int &x, int &y){//扩欧,记得带地址符
if(!b){
x = 1, y = 0;
return ;
}
exgcd(b, a % b, x, y);
int lx = x, ly = y;
x = y, y = lx - a / b * ly;
}
main(){
scanf("%lld", &n);
for(int i=1;i<=n;i++){
scanf("%lld%lld", &m[i], &a[i]);
lcm *= m[i];
}
for(int i=1;i<=n;i++){
int x, y, li = lcm / m[i];//pi 是 li 的倍数
exgcd(li, m[i], x, y);
x *= li * a[i];//你懂得
(ans += x) %= lcm;//先取模,最后再处理答案
}
ans = (ans % lcm + lcm) % lcm;//处理答案
printf("%lld\n", ans);
return 0;
}
极其简短且好记,但是重在弄懂原理。
Part 2:扩展中国剩余定理(EXCRT)
仍然解决的是形如:
的方程组的情况,只不过这次
显然上述方法并不可行且没有太多优化空间,只能考虑新方法。
考虑若只有两个方程组:
设
合并后两个式子,
此时根据裴蜀定理,若
但我们要最小化
然后我们的这两个方程等价于
这个方程看起来没什么用,因为它在说废话。
但是如果现在有
也就是说,exCRT 的核心思想就是两两方程合并。
void exgcd(ll a, ll b, ll &x, ll &y){
if(!b)
return x = 1, y = 0, void();
exgcd(b, a % b, x, y);
ll xx = x, yy = y;
x = yy, y = xx - (a / b) * yy;
}
int main(){
scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%lld%lld", &a[i], &b[i]);
ll nowa = a[1], nowb = b[1];
for(int i=2;i<=n;i++){
ll x, y, g = __gcd(a[i], nowa);
exgcd(nowa, a[i], x, y);//y 用不到,只取 x
x *= (b[i] - nowb) / g;
x = (x % (a[i] / g) + (a[i] / g)) % (a[i] / g);
y = nowa * x + nowb;
nowa = lcm(a[i], nowa);
nowb = (y % nowa + nowa) % nowa;
}
printf("%lld\n", nowb);
return 0;
}
加个乘法改加法,防溢出,即可过洛谷板子 P4777。