拓展欧几里得

欧几里得算法

原理

辗转相除法:用aa除以bb,得到余数rr和结果qq,再用除数bb除以余数rr得到新余数r2r2,再用除数rr除以r2...r2...直至余数为0此时的除数即为最大公因数。

辗转相除法的依据:

gcd(a,b)=gcd(b,a%b)gcd(a,b)=gcd(b,a\%b)

应用

求两个数的最大公约数。

拓展欧几里得

裴属定理

1 1​cpp

a0x0+b0y0=gcd(a0,b0)a_0x_0+b_0y_0=gcd(a_0,b_0)

即如果ax+by=max+by=m有解,那么mm一定是gcd(a,b)gcd(a,b)的整数倍。

解释

联系辗转相除法的依据,我们可以得到:

b0x1+(a0%b0)y1=gcd(a0,b0)=gcd(b0,b0%a0)b_0x_1+(a_0\%b_0)y_1=gcd(a_0,b_0)=gcd(b_0,b_0\%a_0)

a1=b0b1=a0%b0a_1=b_0,b_1=a_0\%b_0

当除数为0时,被除数为最大公约数此时xn=1,yn=0x_n=1,y_n=0,之后我们可以一层层往前推出x0,y0x_0,y_0

xi=yi+1x_i=y_{i+1}

yi=xi+1(ai/bi)yi+1y_i=x_{i+1}-(a_{i}/b_{i})*y_{i+1}

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int ex_gcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1;
y = 0;
return a;
}
int gcd = ex_gcd(b, a % b, x, y);
int temp = y;
y = x - (a / b) * y;
x = temp;
return gcd;
}

求解线性同余方程

定义

形如

axn(modb)ax\equiv n\pmod b

的方程称为线性同余方程。其中a,b,na,b,n为给定整数,xx为未知数。

扩展欧几里得求解

axn(modb)ax\equiv n\pmod b等价于ax+by=nax+by=n,此方程有整数解的充要条件是n%gcd(a,b)==0n \% gcd(a,b)==0(裴属定理)

我们可以通过扩展欧几里得求得一组解(n=gcd(a,b)n=gcd(a,b)时)x0,y0x_0,y_0

此时可以得到:

x=x0ngcd(a,b)x=\frac{x_0*n}{gcd(a,b)}

此特解加上$r=\frac{b}{gcd(a,b)} 的整数倍再模的整数倍再模b$即为通解。

最小正整数解为:

xmin=(x%r+r)%rx_{min}=(x\%r+r)\%r

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
void Equation(int a, int b, int n)
{
int x, y;
int d = ex_gcd(a, b, x, y);
if (n % d != 0)
return;
x = x * n / d;
int r = b / d;
for (int i = 0; i < d; i++)
{
printf("%d\n", (x + i * r) % b);
}
}