扩展欧几里得算法详解

说在前面

先说一下欧几里得算法:

欧几里得算法是计算两个数的最大公约数,这里不详讲,只给出欧几里得算法的代码实现:

int gcd(int a,int b)
{
    if(b == 0)
    {
        return a;
    }
    return gcd(b,a%b);
}

时间复杂度为:log(n)

扩展欧几里得算法的描述是:一定存在整数 x, y 满足等式 a * x + b * y = gcd(a,b)

这里的a,b是已知,gcd(a,b)表示的是a和b的最大公约数,所以扩展欧几里得算法既可以计算出最大公约数gcd(a,b),又可以计算出变量x,y的一组解。如何求解呢?

计算最大公约数的下一步,带入等式:gcd(b, a % b) = bx1 + (a % b) * y
即就是:gcd(b, a % b) = bx1 + (a - (a / b) * b) * y1
gcd(b, a % b) = a * y1 + b * (x1 - (a / b) * y1 )
在回过头来看看等式 当b不等于0时:gcd(a,b) = gcd(b,a%b)
所以,x = y1 ; y = x1 - (a / b) * y1

所以可以递归的求解x,y。那退出条件是什么呢?
我们可以看到当b == 0时,求出最大公约数。考虑一下当b == 0时,a * x = gcd(a, 0) = a。
此时,x == 1,y取0即可计算出一组特解。

	//数组arr[0]是 x, arr[1]是 y
	//其实c代码更简单,只需要int &x 和 int &y 就好了
	//从这就可以看出,c在做简单工程时,即高效又好用。	
	private static int exGcd(int a,int b,int arr[])
    {
        if(b == 0)
        {
            arr[0] = 1;
            arr[1] = 0;
            return a;
        }
        int ans = exGcd(b,a%b,arr);
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp - a/b * arr[1];
        return ans;
    }

时间复杂度为:log(n)

扩展欧几里得算法可以计算二元一次方程组是否存在整数解,并且求出一组解

欧几里得算法只能用来求整数解(因为最大公约数的缘故)

二元一次方程:ax + by = c

1)当a == 0 或 b == 0的时候,方程转为一元一次方程,可直接求得。

2)当c不是 gcd(a,b)的倍数的时候,方程无解。
因为a和b都是他因子的倍数,这个毫无疑问,所以ax+by(a, b的线性组合)也是gcd(a, b)的倍数。如果c不是gcd(a, b)的倍数,那么两边不相等,无解。这条结论叫做“裴蜀定理”。

3)当c是gcd(a, b)的倍数,设g = gcd(a, b),则用ax + by = g求解出的一组解(x0,y0),有ax + by = c 求出的一组解为( x 0 c g \frac{x_0c}{g} gx0c, y 0 c g \frac{y_0c}{g} gy0c

如果你理解了,可以挑战一下 青蛙的约会 这道题。
题解:http://blog.csdn.net/SwordHoly/archive/2009/08/07/4423543.aspx可以参考一下

用扩展欧几里得算法求乘法逆元

这里先说一下乘法逆元,我们都知道,‘取模%’ 具有加、减、乘法的分配率即:
1). (a+b) % c = ((a%c) + (b%c))%c
2). (a-b) % c = ((a%c) - (b%c))%c
3). (a × \times ×b) % c = ((a%c) × \times × (b%c))%c
这样的好处就是,如果a+b+…+n计算出来的值太大的话,可以通过分配率来计算出每一步的值在求余。
除法没有分配率,但是有乘法逆元。就是将除法转化为乘法。

逆元:设c是b的逆元,则有ax ≡ \equiv 1(mod p);
(a/b)mod p = (a/b) * 1(mod p) = (a/b)bx (mod p) = a
x(mod p)

定理:a存在模p的乘法逆元的充要条件是gcd(a,p) = 1;(定理的证明在这里就不叙述了)

对于ax + by = 1,可以看出x是a模b的乘法逆元,y是b模a的乘法逆元。

你可能感兴趣的:(数据结构和算法,算法,gcd)