欧几里得算法——求最大公约数:
使用辗转相除法。
// 欧几里得——递归
int gcd1(int a, int b)
{
return b==0 ? a : gcd1(b, a%b);
}
// 欧几里得——非递归
int gcd2(int a, int b)
{
int t;
while(b!=0){
t = a;
a = b;
b = t%b;
}
return a;
}
扩展欧几里得算法
扩展欧几里得算法不仅能确定两个正整数的最大公约数,如果这两个数互素,还能确定它们的逆元。
若
gcd(a,b)=1
, 那么 a 有一个模 n 的乘法逆元:
a−1
。即
a∗a−1=1modb
;
同时满足方程:
a∗x+b∗y=1
,存在一组(x,y)解。
例子
用类似辗转相除法,求二元一次不定方程 47x+30y=1 的整数解。(递归)
47 = 30 * 1 + 17
30 = 17 * 1 + 13
17 = 13 * 1 + 4
13 = 4 * 3 + 1
然后把它们改写成“余数等于”的形式
17 = 47 * 1 + 30 * (-1) //式1
13 = 30 * 1 + 17 * (-1) //式2
4 = 17 * 1 + 13 * (-1) //式3
1 = 13 * 1 + 4 * (-3)
然后把它们“倒回去”
1 = 13 * 1 + 4 * (-3)
1 = 13 * 1 + [17 * 1 + 13 * (-1)] * (-3) //应用式3
1 = 17 * (-3) + 13 * 4
1 = 17 * (-3) + [30 * 1 + 17 * (-1)] * 4 //应用式2
1 = 30 * 4 + 17 * (-7)
1 = 30 * 4 + [47 * 1 + 30 * (-1)] * (-7) //应用式1
1 = 47 * (-7) + 30 * 11
得解x=-7, y=11。
// 扩展欧几里得(递归),若返回 gcd=1,则 a^-1 = x
int exEuclid1(int a, int b, int &x, int &y)
{
if(b==0){ //上一个 b = 1, a % b = 0
x = 1;
y = 0;
return a; // a 即上一个 b,此时 a = 1,则 gcd=1
}
int g = exEuclid1(b, a%b, x, y);
int t = x;
x = y;
y = t - a/b * y;
return g;
}
当前式子:
a∗x+b∗y=gcd
下次循环:
b∗x1+(a%b)∗y1=gcd
化简整理:
a∗y1+b∗(x1–a/b∗y1)=gcd
// 扩展欧几里得(非递归),若返回 gcd=1,则 a^-1 = x(即y1)
int exEuclid2(int a, int b, int &x, int &y)
{
int x1=1, x2=0, x3=a; //47 1 0
int y1=0, y2=1, y3=b; //30 0 1
int t1, t2, t3;
int q;
while (1){
if (y3 == 0) {
x = x1; y = x2; // y3 即上一个循环的余数,y3=0 说明无逆元
return x3;
}
else if (y3 == 1){ // y3=1 即上一个循环的余数为 1,有逆元
x = y1;
y = y2;
return y3;
}
q = x3/y3;
t1=x1-q*y1; t2=x2-q*y2; t3=x3-q*y3;
x1=y1; x2=y2; x3=y3;
y1=t1; y2=t2; y3=t3;
}
}
参考资料:扩展欧几里得算法 - 维基百科