//x为a%b的逆元,y为b%a的逆元,结果可能会小于零,若小于零,手动x+=b,y+=a
//只有当a,b互质时(即gcd的返回值为1)x,y才有效
int ex_gcd(int a, int b, int &x, int &y)
{
if (b == 0)//b==0时得到gcd的结果,即a'=gcd(a,b)这里的a'和a是两个值,所以x=1
{
//一组特解,y可以取任意数,但是y=0时得到的逆元是最小的
x = 1;
y = 0;
return a;
}
int ans = ex_gcd(b, a % b, x, y);
//由最下面一层递归往回计算x,y的值
int t = x;
x = y;
y = t - (a / b) * y;
return ans;//返回最小公约数
}
用这个算法来求解中国剩余定理
参考了一位博主的办法:博主文章链接
第一步:
先找出三个数,分别是a和b的公倍数中能被c除余1的最小数,a和c的公倍数中能被b除余1的最小数,b和c的公倍数中能被a除余1的最小数。
以a和b的公倍数中能被c除余1的最小数为例:只要将a,b的最小公倍数乘上对c(mod)的逆元即可:
ex_gcd(a*b,c,x,y);
int num1=a*b/gcd(a,b)*x;
对于剩下两个数也做一样的处理。得到num2,num3。
ex_gcd(a*c,b,x,y);
int num2=a*c/gcd(a,c)*x;
ex_gcd(b*c,a,x,y);
int num3=b*c/gcd(b,c)*x;
然后第二步:
num1=num1*o;
num2=num2*n;
num3=num3*m;
第三步:
将三个数加起来除以a,b,c的最小公倍数取余就可得到x的最小值
int gbs=a*b*c/gcd(gcd(a,b),c);
int ans=(num1+num2+num3)%gbs;
以一个例子来验证一下:
#include <bits/stdc++.h>
using namespace std;
int ex_gcd(int a, int b, int &x, int &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
int ans = ex_gcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return ans;
}
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int china_rest(int a, int b, int c, int m, int n, int o)
{
int x, y;
//第一步
ex_gcd(a * b, c, x, y);
int num1 = a * b / gcd(a, b) * x;
ex_gcd(a * c, b, x, y);
int num2 = a * c / gcd(a, c) * x;
ex_gcd(b * c, a, x, y);
int num3 = b * c / gcd(b, c) * x;
//第二步
num1*=o;
num2*=n;
num3*=m;
//第三步
int gbs=a*b*c/gcd(gcd(a,b),c);
return (num1+num2+num3)%gbs;
}
int main()
{
int m,n,o,a,b,c;
cin>>a>>b>>c>>m>>n>>o;
cout<<china_rest(a,b,c,m,n,o)<<endl;
return 0;
}
以下为输入和输出结果
那么,如果将式子改到四个或更多呢?以四个为例
实际上我们可以先算前面三个,当成普通的中国剩余问题来算,那么最终结果一定是前面三个式子结果的倍数。
那么问题就转换成了一个式子的问题,从而又一次回到了逆元的计算。
假设t对d(mod)的逆元为q,那么
k*t=t*q*p/(a*b*c*d)/gcd(gcd(a,b),gcd(c,d));
对于更多的式子,只要重复这样的过程,不断往后算即可。