先说一下欧几里得算法:
欧几里得算法是计算两个数的最大公约数,这里不详讲,只给出欧几里得算法的代码实现:
int gcd(int a,int b)
{
if(b == 0)
{
return a;
}
return gcd(b,a%b);
}
时间复杂度为:log(n)
这里的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) = ax(mod p)
定理:a存在模p的乘法逆元的充要条件是gcd(a,p) = 1;(定理的证明在这里就不叙述了)
对于ax + by = 1,可以看出x是a模b的乘法逆元,y是b模a的乘法逆元。