扩展中国剩余定理(EXCRT)

本文详细介绍了扩展中国剩余定理(EXCRT),用于解决不完全互素的一元线性同余方程组。通过扩展欧几里得算法找到整数解,并给出算法流程,包括如何避免数据溢出。最后提供了模板代码供参考。

扩展中国剩余定理(EXCRT)

中国剩余定理可以解形如下面的一元线性同余方程组:
{ans≡x1(moda1)ans≡x2(moda2)ans≡x3(moda3)...ans≡xn(modan) \left\{\begin{matrix} ans\equiv x_1\pmod{a_1} \\ ans\equiv x_2\pmod{a_2} \\ ans\equiv x_3\pmod{a_3}\\ .\\ .\\ .\\ ans\equiv x_n\pmod{a_n} \end{matrix}\right.ansx1(moda1)ansx2(moda2)ansx3(moda3)...ansxn(modan)
其中a1,a2,a3…an两两互素。

如果a1,a2,a3…an不两两互素时,就不能利用中国剩余定理去解上述方程组了。因为不存在另外n-1个ai的乘积关于剩下一个数的逆元。

我们先考虑将如下方程组合并成一个方程
{ans≡x1(moda1)ans≡x2(moda2)\left\{\begin{matrix} ans\equiv x_1\pmod{a_1} \\ ans\equiv x_2\pmod{a_2} \\ \end{matrix}\right.{ansx1(moda1)ansx2(moda2)

该方程存在唯一解ans,使得ans=m1a1+x1=m2a2+x2ans=m_1a_1+x_1=m_2a_2+x_2ans=m1a1+x1=m2a2+x2成立。
所以有方程
m1a1+x1=m2a2+x2m_1a_1+x_1=m_2a_2+x_2m1a1+x1=m2a2+x2
m1a1−m2a2=x2−x1m_1a_1-m_2a_2=x_2-x_1m1a1m2a2=x2x1
成立。
如果有gcd(a1,a2)∣(x2−x1)gcd(a_1,a_2)|(x_2-x_1)gcd(a1,a2)(x2x1),我们则可以通过扩展欧几里得算法求得整数解。
否则无解。

在求得一组解(m1,m2)后,可以将上述方程组转化为以下方程:
ans≡X(modlcm(a1,a2))ans\equiv X\pmod {lcm(a_1,a_2)}ansX(modlcm(a1,a2))
其中X=m1a1+b1=m2a2+b2X=m_1a_1+b_1=m_2a_2+b_2X=m1a1+b1=m2a2+b2

显然X是上述方程组的一个特解。
M=lcm(a1,a2)M=lcm(a_1,a_2)M=lcm(a1,a2),那么通解为X′=kM+XX'=kM+XX=kM+X

可以将上述结论推广至由k个同余方程组成的一元线性同余方程组。

算法流程如下:
设xk为前k个一元线性同余方程合并后的一个特解,xk+1为第k+1个同余方程的特解。ak为前k个一元线性同余方程合并后的模数,即lcm(a1,a2,...,ak)lcm(a1,a2,...,a_k)lcm(a1,a2,...,ak)。ak+1为第k+1个同余方程的模数。
for k=1 to n:for\space k=1\space to\space n:for k=1 to n:
1、解方程akx−ak+1y=gcd(ak,ak+1)a_kx-a_{k+1}y=gcd(a_k,a_{k+1})akxak+1y=gcd(ak,ak+1)
2、若gcd(ak,ak+1)∣(xk+1−xk)gcd(a_k,a_{k+1})|(x_{k+1}-x_k)gcd(ak,ak+1)(xk+1xk),则可以求出akx−ak+1y=xk+1−xka_kx-a_{k+1}y=x_{k+1}-x_kakxak+1y=xk+1xk的一个特解x′=xk+1−xkgcd(ak,ak+1)xx'=\frac{x_{k+1}-x_k}{gcd(a_k,a_{k+1})}xx=gcd(ak,ak+1)xk+1xkx;否则无解,算法结束。
3、合并前k+1个方程的后一个特解为xk+1=akx′+xkx_{k+1}=a_kx'+x_kxk+1=akx+xk,
4、合并前k+1个方程的后的模数为lcm(ak,ak+1)lcm(a_k,a_{k+1})lcm(ak,ak+1)

知道对于方程ax+by=gcd(a,b)ax+by=gcd(a,b)ax+by=gcd(a,b),由一对特解(x,y),那么由通解X,Y,X=k⋅bgcd(a,b)+x,Y=k⋅agcd(a,b)+yX=k\cdot\frac{b}{gcd(a,b)}+x,Y=k\cdot\frac{a}{gcd(a,b)}+yX=kgcd(a,b)b+x,Y=kgcd(a,b)a+y
在上述算法流程中,如果xk+1−xkgcd(ak,ak+1)\frac{x_{k+1}-x_k}{gcd(a_k,a_{k+1})}gcd(ak,ak+1)xk+1xk较大,直接与x相乘可能会造成溢出。我们可以把x’限制在[0ak+1gcd(ak,ak+1)][0\frac{a_{k+1}}{gcd(a_k,a_{k+1})}][0gcd(ak,ak+1)ak+1]中,通过慢速乘法(类似与快速幂)防止数据溢出。

模板代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){
		x=1;y=0;d=a;return ;
	}
	else{
		exgcd(b,a%b,d,y,x);y-=(a/b)*x;
	}
}
ll mul(ll a,ll b,ll mod)
{
	ll ans=0;
	while(b)
	{
		if(b&1)ans=(ans+a)%mod;
		a=(a+a)%mod;
		b>>=1;
	}
	return ans;
}
int main(void)
{
	int n;
	scanf("%d",&n);
	ll b1=0,a1=1;
	ll b2,a2;
	while(n--)
	{
		scanf("%lld%lld",&a2,&b2);
		ll x,y,d,t;//求解x*a1+b1=b2(mod a2) 
		t=((b2-b1)%a2+a2)%a2;//x*a1=b2-b1(mod a2)
		exgcd(a1,a2,d,x,y);//求解x*a1+y*a2=gcd(a1,a2) 
		ll g=a2/d;
		x=mul(x,t/d,g);
		b1+=x*a1;
		a1*=a2/d;//lcm
		b1=(b1%a1+a1)%a1;
	}
	printf("%lld\n",b1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值