文章原链接点击打开链接
欧几里得算法
欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数 gcd(a,b)。基本算法:设 a = qb + r,其中a,b,q,r都是整数,则 gcd(a,b) = gcd(b,r),即 gcd(a,b) = gcd(b,a%b)。
证明:
a = qb + r
如果 r = 0,那么 a 是 b 的倍数,此时显然 b 是 a 和 b 的最大公约数。
如果 r ≠ 0,任何整除 a 和 b 的数必定整除 a - qb = r,而且任何同时整除 b 和 r 的数必定整除 qb + r = a,所以 a 和 b 的公约数集合与 b 和r 的公约数集合是相同的。特别的,a 和 b 的最大公约数是相同的。
递归实现:
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a%b);
}
非递归实现:
int gcd(int a, int b)
{
while(b)
{
int t = b;
b = a % b;
a = t;
}
return a;
}
扩展欧几里得算法
扩展欧几里得算法就是在求 a,b 的最大公约数 m=gcd(a,b) 的同时,求出贝祖等式ax + by = m的一个解 (x,y)。
扩展欧几里得算法的递归实现:
有两个数 a,b,对它们进行辗转相除法,可得它们的最大公约数——这是众所周知的。然后,收集辗转相除法中产生的式子,倒回去,可以得到 ax+by=gcd(a,b)的整数解。
先来看下这个几乎所有总结扩展欧几里得算法的帖子中都会用到的例子
(可能出自wikipedia,毕竟wikipedia上也是用的这个栗子):
用类似辗转相除法,求二元一次不定方程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) //应用式3
1=13*1+[17*1+13*(-1)]*(-3)
1=13*4+17*(-3) //应用式2
1=[30*1+17*(-1)]*4+17*(-3)
1=30*4+17*(-7) //应用式1
1=30*4+[47*1+30*(-1)]*(-7)
1=30*11+47*(-7)
得解x=-7, y=11。
ax+by
=
gcd(a,b)
设:a>b。
推理1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;//推理1
推理2,ab!=0 时
设 ax1+by1=gcd(a,b);
bx2+(a mod b)y2=gcd(b,a mod b);
根据朴素的欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
则:ax1+by1=bx2+(a mod b)y2;
即:ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;
根据恒等定理得:x1=y2 ,y1=x2-(a/b)*y2;//推理2
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
这样我们就找到了递推关系:
x1=y2;
y1=x2−(a/b)∗y2;
递推的终止条件再上面也已经给出:
当b=0,gcd(a,b)=a。此时x=1,y=0;
由此我们可以得到递归的扩展欧几里得算法的代码:
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{//推理1,终止条件
x = 1;
y = 0;
return a;
}
int r = exgcd(b, a%b, x, y);
//先得到更底层的x2,y2,再根据计算好的x2,y2计算x1,y1。
//推理2,递推关系
int t = y;
y = x - (a/b) * y;
x = t;
return r;
}
比如上面的 47x+30y=1 的例子,exgcd() 要求出 gcd(47, 30),同时得到一组 (x,y)的解。
用一个方便我自己理解的方式,把求解的过程写出来好了。这里的大括号"{}"里面不是函数体,"{"表示一层递归调用的开始,"}"表示该层递归的结束。
exgcd(47, 30, x, y)
{
r = exgcd(30, 17,x, y)
{
r = exgcd(17, 13, x, y)
{
r = exgcd(13, 4, x, y)
{
r = exgcd(4, 1, x, y)
{
r = exgcd(1, 0, x, y)
{
x = 1;
y = 0;
return 1;
}
t = y = 0;
y = x - (4/1) * y = 1;
x = t = 0;
return r = 1;
}
t = 1;
y = 0 - (13/4) * 1 = -3;
x = 1;
return 1;
}
t = -3;
y = 1 - (17/13) * (-3) = 4;
x = -3;
return 1;
}
t = 4;
y = -3 - (30/17) * 4 = -7;
x = 4;
return 1;
}
t = -7;
y = 4 - (47/30) * (-7) = 11;
x = -7;
return 1;
}
最后的结果:
r = exgcd(47,30,x,y) = 1;
x = -7;
y = 11;