欧几里德与扩展欧几里德算法

欧几里德算法

欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。

基本算法:设a = qb + r,其中a,b,q,r都是整数,则gcd(a, b) = gcd(b, r),即gcd(a, b) = gcd(b, a % b)

算法的实现:

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

扩展欧几里德算法

扩展的欧几里得算法用于计算满足形如a*x+b*y=c 的方程的整数
首先,我们需要先判断这个方程有没有解
对于形如a*x+b*y=c 的方程,有解的条件是c是a和b最大公约数的倍数
c = n*gcd(a, b)(n > 0)

在判定有解之后,我们首先需要计算出一组满足条件的(x, y),由于a, b, c都是gcd(a, b)的整数倍,我们可以将它们都缩小gcd(a, b)倍,令A=a/gcd(a,b), B=b/gcd(a,b), C=c/gcd(a,b); 而缩小后的方程与原方程是同解的。

所以原式可以化简为A*x+B*y=C,其中gcd(A, B) == 1
此时,我们可以先求A*x+B*y=1的解(x’, y’),然后将其扩大C倍,最后要求的解就是(x, y)=(C*x’, C*y’)

接下来就是研究如何解A*x+B*y=1
A*x+B*y=gcd(A, B);
假设A>B>0,我们设

A * x[1] +  B * y[1] = gcd(A, B);
B * x[2] + (A mod B) * y[2] = gcd(B, A mod B);

而根据欧几里得算法,我们知道gcd(A, B)==gcd(B, A mod B)
所以有下面的推理过程:

    A * x[1] + B * y[1] = B * x[2] + (A mod B) * y[2]
=>  A * x[1] + B * y[1] = B * x[2] + (A - kB) * y[2]    // A = kB + r
=>  A * x[1] + B * y[1] = A * y[2] + B * x[2] - kB * y[2]
=>  A * x[1] + B * y[1] = A * y[2] + B * (x[2] - ky[2])
=>  x[1] = y[2],  y[1] = (x[2] - ky[2])

利用这个性质,我们可就以递归的去求解(x,y)了。

其终止条件为B = 0(也就是在递归过程中用gcd求出了最大公约数的时候),此时可以算出对应的(x,y)=(1, 0) 。然后根据递归, 从gcd(d,0)往前处理,在进行欧几里德算法的递归的时候根据相邻两次调用间x, y和x’, y’的关系计算即可求出ax + by = gcd(a, b)的解.
代码如下:

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

但是上面只是求出了一组满足条件的解(X0, Y0) ,但我们通常都需要用到最小非负的X0,我们又该需要怎么做呢
如果那样的话,我们需要将(A,B,x’,y’)扩充为一个解系。
由于A B是互质的,所以可以将A*x’+B*y’=1扩展为:

AX0+BY0+(u+(-u))AB=1
=>  (X0 + uB)*A + (Y0 - uA)*B = 1
=>  X = X0 + uB, Y = Y0 - uA

所以可以求得最小的X为(X0 + uB) mod B,(X0 + uB > 0)
同时我们还需要将X扩大C倍,因此最后解为:
X = (X * C) mod B
若x<0,则不断累加B,直到x>0为止。
总体代码如下:

int solve(int a, int b, int c)
{
    int d = gcd(a, b);
    if (c % d)
    {
        return -1;
    }
    a /= d, b /= d, c /= d;
    int x, y;
    exgcd(a, b, x, y);
    x = ((x * c) % b + b) % b;
    /*
    x = (x * c) % b;
    while (x < 0) x += b;   //更为通俗的写法
    */
    return x;
}

最后提一句,涉及数论的题目一般数据很大,所以一般用long long类型,而不用int型

你可能感兴趣的:(数论)