中国剩余定理

中国剩余定理

前置知识

逆元(扩展欧几里得算法,费马小定理),模意义下的四则运算

介绍

中国剩余定理主要用于解决这样的线性同余方程组,其中 b1,b2,,bn 两两互质

{xa1(modb1)xa2(modb2)  xan(modbn)

对于这样的方程组我们只需要求出 x 的一组特解,把这个特解加上 b1,b2,,bn 的最小公倍数的任意整数倍就可以得到所有解,所以我们就只需要考虑怎么构造出一组特解

例子

下面举个例子来帮助理解

{x2(mod3)x4(mod7)x5(mod8)

直接构造不太行,但是对于每一个线性同余方程构造出一个特解是非常轻松的,所以我们先设第 i 个式子的解是 yi,那么有

{y12(mod3)y24(mod7)yn5(mod8)

最终的 x 一定是 yi 以各种方式组合起来,那么问题就是怎么组合才能得到 x,如果 yi 之间相乘就把问题变得更复杂了,所以我们把它们加起来,想办法凑出 x

如果 x=y1+y2+y3,那么每一个 y 需要满足什么条件呢?

因为 y1+y2+y3y12(mod3),所以 y2+y3 一定是 3 的倍数,当 y2,y3 分别都是 3 的倍数时这一定也是满足条件的一个特解

所以我们不妨假设 y2,y3 都是 3 的倍数

同理 y1,y3 都是 7 的倍数,y1,y2 都是 8 的倍数

那我们就可以把 y1 写成 56z1,把 y2 写成 24z2,把 y3 写成 21z3

原方程就变成了这样

{56z12(mod3)24z24(mod7)21z35(mod8)

然后就非常好求了,只需要求出

{56w11(mod3)24w21(mod7)21w31(mod8)

也就是 21,24,56 分别对于 3,7,8 的乘法逆元,然后在等式两边同时分别乘以 2,4,5 就可以得到 z1,z2,z3 的一组特解

用扩展欧几里得算法求出 w1=2,w2=5,w3=5,所以 z1=2w1=4,z2=4w2=20,z3=5w3=25

于是 y1=56z1=224,y2=24z2=480,y3=21z3=525

最后就得到 x=y1+y2+y3=1229,由于 3,7,8 的最小公倍数是 168,我们将 1229 对于 168 取模就能得到 x 的最小正整数解 53

x 的通解就能表示为 53+168k,kZ

数学语言

上面的例子比较好理解,下面用更严谨的数学语言再来描述一遍,没兴趣可以直接跳过

直接构造不太行,但是对于每一个线性同余方程构造出一个特解是非常轻松的,所以我们先设第 i 个式子的解是 yi,那么有

{y1a1(modb1)y2a2(modb2)  ynan(modbn)

最终的 x 一定是 yi 以各种方式组合起来,如果 yi 之间相乘就把问题变得更复杂了,所以我们把它们加起来以凑出 x

如果 x=i=1nyi,那么考虑每一个 y 需要满足的条件

由于 ytat(modbt)x=i=1nyiat(modbt),所以 yti=1nyi(modbt)

因为只要构造一组特解所以我们不妨假设 i=1nyi[it]0(modbt),也就是 b|i=1nyi[it]

用人话说就是 yt 在加上所有其他 y 之后对于 bt 取模的结果仍然不变,所以其他所有 y 的和一定是 bt 的倍数,因为只要构造一组特解所以我们不妨假设其他每一个 y 都是 bt 的倍数

不妨令 pt=i=1nbi[it]yt=ptzt

原方程组就转换为了

{p1z1a1(modb1)p2z2a2(modb2)  pnznan(modbn)

假设 pt 在模 bt 意义下的乘法逆元为 wt,那么根据乘法逆元的定义 ptwt1(modbt),两边同时乘以 at 就有 ptatwtat(modbt),所以 atwt 一定是 zt 的一个特解

那么对于 ptztat(modbt)zt 一定有一个特解为 pt 在模 bt 意义下的乘法逆元的 at

p 可以直接算出来,然后利用扩展欧几里得算法(b 为质数时也可以使用费马小定理)求出 w,乘上 a 就能得到 z,再将 z 乘上 p 就能得到 y,全部加起来就可以算出 x 的一个特解了,只需要模 b1,b2,,bn 的最小公倍数就可以得到最小正解

C++ Code

模板题目传送门 Luogu P1495

#include<bits/stdc++.h>
#define in read()
using namespace std;
typedef long long ll;
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<48)c=getchar();
    while(c>47)x=(x*10)+(c^48),c=getchar();
    return x;
}
inline void mwrite(ll a)
{
    if(a>9)mwrite(a/10);
    putchar((a%10)|48);
}
inline void write(ll a,char c)
{
	mwrite(a);
	putchar(c);
}
//上面是IO优化 
const int MAXN=15;
int n,c[MAXN],d[MAXN];
inline void exgcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得 
{
    if(!b) return x=1,y=0,void(0);
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}
inline ll inv(ll x,ll p)//求逆元 
{
	ll X,Y;
	exgcd(x,p,X,Y);
	return (X%p+p)%p;
}
inline ll crt()//CRT
{
	ll prod=1,ans=0;
	for(int i=1;i<=n;++i) prod*=d[i];
	ll tmp;
	for(int i=1;i<=n;++i)
	{
		tmp=prod/d[i];
		ans+=(inv(tmp,d[i])*c[i]*tmp)%prod;
	}
	return ans%prod;
} 
signed main()
{
	n=in;
	for(int i=1;i<=n;++i) d[i]=in,c[i]=in;
	write(crt(),'\n');
    return 0;
}

博客园传送门

知乎传送门

posted @   人形魔芋  阅读(145)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录导航