拓展欧几里得
欧几里得算法
原理
辗转相除法:用a a a 除以b b b ,得到余数r r r 和结果q q q ,再用除数b b b 除以余数r r r 得到新余数r 2 r2 r 2 ,再用除数r r r 除以r 2... r2... r 2 . . . 直至余数为0此时的除数即为最大公因数。
辗转相除法的依据:
g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b)
g c d ( a , b ) = g c d ( b , a % b )
应用
求两个数的最大公约数。
拓展欧几里得
裴属定理
1 1cpp
a 0 x 0 + b 0 y 0 = g c d ( a 0 , b 0 ) a_0x_0+b_0y_0=gcd(a_0,b_0)
a 0 x 0 + b 0 y 0 = g c d ( a 0 , b 0 )
即如果a x + b y = m ax+by=m a x + b y = m 有解,那么m m m 一定是g c d ( a , b ) gcd(a,b) g c d ( a , b ) 的整数倍。
解释
联系辗转相除法的依据,我们可以得到:
b 0 x 1 + ( a 0 % b 0 ) y 1 = g c d ( a 0 , b 0 ) = g c d ( b 0 , b 0 % a 0 ) b_0x_1+(a_0\%b_0)y_1=gcd(a_0,b_0)=gcd(b_0,b_0\%a_0)
b 0 x 1 + ( a 0 % b 0 ) y 1 = g c d ( a 0 , b 0 ) = g c d ( b 0 , b 0 % a 0 )
a 1 = b 0 , b 1 = a 0 % b 0 a_1=b_0,b_1=a_0\%b_0
a 1 = b 0 , b 1 = a 0 % b 0
当除数为0时,被除数为最大公约数此时x n = 1 , y n = 0 x_n=1,y_n=0 x n = 1 , y n = 0 ,之后我们可以一层层往前推出x 0 , y 0 x_0,y_0 x 0 , y 0 。
x i = y i + 1 x_i=y_{i+1}
x i = y i + 1
y i = x i + 1 − ( a i / b i ) ∗ y i + 1 y_i=x_{i+1}-(a_{i}/b_{i})*y_{i+1}
y 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; }
求解线性同余方程
定义
形如
a x ≡ n ( m o d b ) ax\equiv n\pmod b
a x ≡ n ( m o d b )
的方程称为线性同余方程 。其中a , b , n a,b,n a , b , n 为给定整数,x x x 为未知数。
扩展欧几里得求解
a x ≡ n ( m o d b ) ax\equiv n\pmod b a x ≡ n ( m o d b ) 等价于a x + b y = n ax+by=n a x + b y = n ,此方程有整数解的充要条件是n % g c d ( a , b ) = = 0 n \% gcd(a,b)==0 n % g c d ( a , b ) = = 0 (裴属定理) 。
我们可以通过扩展欧几里得求得一组解(n = g c d ( a , b ) n=gcd(a,b) n = g c d ( a , b ) 时)x 0 , y 0 x_0,y_0 x 0 , y 0 。
此时可以得到:
x = x 0 ∗ n g c d ( a , b ) x=\frac{x_0*n}{gcd(a,b)}
x = g c d ( a , b ) x 0 ∗ n
此特解加上$r=\frac{b}{gcd(a,b)} 的整数倍再模 的整数倍再模 的 整 数 倍 再 模 b$即为通解。
最小正整数解为:
x m i n = ( x % r + r ) % r x_{min}=(x\%r+r)\%r
x m i n = ( 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); } }