扩展欧几里得算法——exgcd

扩展欧几里德算法是用来在已知 a , b a,b 求解一组 x , y x,y ,使它们满足贝祖(裴蜀)等式: a x + b y = g c d ( a , b ) = d ax+by = gcd(a, b) =d

关于贝祖等式

试着来搞一下

a x + b y = g c d ( a , b ) ax+by=gcd(a,b)

先考虑一下特殊情况,如果 b = 0 b=0 ,那么 g c d ( a , b ) = a gcd(a,b)=a ,显然存在一组解 x = 1 , y = 0 x=1,y=0

设当前的式子为 a x 1 + b y 1 = g c d ( a , b ) ax_1+by_1=gcd(a,b) ,肯定存在式子 b x 2 + ( a bx_2+(a % b ) y 2 = g c d ( b , a b)y_2=gcd(b,a % b ) b)

根据欧几里得算法, g c d ( a , b ) = g c d ( b , a gcd(a,b)=gcd(b,a % b ) b)

a x 1 + b y 1 = b x 2 + ( a \therefore ax_1+by_1=bx_2+(a % b ) y 2 b)y_2

a \because a % b = a a / b b b=a-a/b*b (这里的除是指整除)

a x 1 + b y 1 = b x 2 + ( a a / b b ) y 2 \therefore ax_1+by_1=bx_2+(a-a/b*b)y_2
    a x 1 + b y 1 = b x 2 + a y 2 a / b b y 2 ax_1+by_1=bx_2+ay_2-a/b*b*y_2
    a x 1 + b y 1 = a y 2 + b ( x 2 a / b y 2 ) ax_1+by_1=ay_2+b(x_2-a/b*y_2)

x 1 = y 2 , y 1 = x 2 a / b y 2 \therefore x_1=y_2,y1=x_2-a/b*y2

于是就可以愉快的递推下去了

代码贼短:

int exgcd(int a,int b,long long &x,long long &y)
{
    if(b==0)return x=1,y=0,a;
    int d=exgcd(b,a%b,y,x);
    return y-=a/b*x,d;
}

顺便普及一下逗号运算符,:在c++中,(a,b,c)==c。也就是说,一堆表达式用逗号连接起来,他们的值就是最后一个表达式的值(巧妙的使用可以使得代码复杂度降低)


当然exgcd不可能只有这么点用途呀,它还可以用来求逆元,并且比用费马小定理求更方便,比如求 a a 在模 p p 意义下的逆元,如果用费马小定理求的话,还要保证 p p 是个质数,但是用exgcd就不用了。

怎么求呢?

x x a a 在模 p p 意义下的逆元,那么满足式子:
a x 1 ax \equiv 1 ( m o d (mod m ) m)

那么有:

a x + m y = 1 ax+my=1

然后用exgcd搞出 a a 即可(以及这就是为什么 a和m一定要互质 才能使得 a a 在模 m m 意义下有逆元)

以及逆元的代码在此:

long long inv(long long a,long long m)
{
    long long x,y;
    long long d=exgcd(a,m,x,y);
    return d==1?(x+m)%n:-1;//不互质就没有逆元
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/84103150