欧几里得:
int gcd(int a, int b)
{
return !b ? a : gcd(b, a%b);
}
int lcm(int a, int b)//最小公倍数
{
return a / gcd(a, b) * b;//先除后乘避免溢出
}
扩展欧几里得:
存在整数对(x,y)使得ax+by=gcd(a,b)
推导过程:
用递归求解扩展欧几里得,设已经求出了下一层递归的解,即:ax1+by1=gcd(a,b)的解(x1,y1)
又a%b=a−(a/b)∗b
将(x1,y1)代入到bx1+(a%b)y1=gcd(a,b)中,得
bx1+(a−(a/b)∗b)∗y1=gcd(a,b) => ay1+b(x1−(a/b)∗y1)=gcd(a,b)
当b=0时,显然有a∗1+b∗0=gcd(a,b)
写成代码,模板如下
int extgcd(int a, int b, int &x, int &y)
{
int d = a;
if(b != 0)
{
d = extgcd(b, a%b, y, x);
y -= (a / b) * x;
}
else x = 1, y = 0;
return d;
}
求解不定方程:
若c%gcd(a,b)=0,则存在整数对(x,y),使得a∗x+b∗y=c
通过上面的方法可得到一组特解(x0,y0)使得a∗x+b∗y=gcd(a,b),那么如何在无穷多个解中求出x,y最小正整数解。
证明:
首先 a∗x0+a∗k∗b/gcd(a,b)+b∗y0−a∗k∗b/gcd(a,b)=gcd(a,b)
即 a∗(x0+k∗b/gcd(a,b))+b∗(y0−k∗a/gcd(a,b))=gcd(a,b)
通解为x=x0+k∗b/gcd(a,b),y=y0−k∗a/gcd(a,b),其中k=...−2,−1,0,1,2...
在所有解中最小的正整数为(x0+b/gcd(a,b)),
所以对于方程a∗x+b∗y=c,最小正整数解(以x为例)为(x0∗c/gcd(a,b)+b/gcd(a,b))
注意:若b为负数,需将b转换为正数。
int cal(int a, int b, int c)
{
int x, y;
int gcd = extgcd(a, b, x, y);
if(c % gcd != 0) return -1;
x *= c/gcd;
b /= gcd;
if(b < 0) b = -b;
int ans = x % b;
if(ans <= 0) ans += b;
return ans;
}
同余方程:
根据上面的内容,我们可以得到:
a∗x≡b(mod n),转化为a∗x+n∗y=b,当b时,方程有 gcd(a,n) 个解。
a∗x≡1(mod n),如果gcd(a,n)=1,则方程有唯一解。
解线性方程ax+by=c
bool cal(int a, int b, int c)
{
int x0, y0;
int d = extgcd(a, b, x0, y0);
if(c % d) return false;
int x = x0*c/d, y = y0*c/d;
kx = b/d, ky = -a/d;
return true; // 解集为:(x+kx*t, y+ky*t),t为整数
}