欧几里得:
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
为例)为
注意:若
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为整数
}