欧几里得算法,也称辗转相除法,是数论中一项基本技术,欧几里得算法提出至今2000多年,仍然是数论科学家们的至爱,它通过一个简单的过程来确定两个整数的最大公因子(greatest common divisor, GCD)。而扩展的欧几里得算法不仅确定两个正整数的最大公因子,还能求出最大公因子关于这两个正整数的一个线性组合(文章中已给出定义)。欧几里得算法也是求解线性同余方程的有得工具,在密码学中有广泛的应用。下面就其算法进行分析,并给出数学证明及算法效率。
在定义欧几里得算法之前,先定义一些基本概念。
定义1(因子) 设a, b是整数,若存在整数d使得b = ad, 则称a是b的一个因子,也称a整除b或称b是a的倍数,记为 a|b。
定义2(最大公因子) 若整数c满足c|a, c|b, 则c称为整数a和b的公因子。a和b的最大公因子记为gcd(a,b),简记(a, b),其定义为:
定理1(欧几里得算法) 要计算两个整数a与b的最大公因数,令r0= a,r1 = b,然后计算相继的商和余数
ri = qi+1×ri+1 + ri+2(i = 0,1,2…)
直到rn+1为0,最后的非零余数rn就是a与b的最大公因子。
算法1(欧几里得算法, Euclid algorithm) 输入两个非负整数a, b,输出最大公因子
(1)令r0←a, r1←b;
(2)if r1 = 0 return r0;
(3)令r2←r0mod r1;
(4)令r0←r1,r1←r2
(5)返回(2)
从定理1的描述中,我们至少有两个问题要弄明白:一是为什么rn+1为0时,最后的非零余数rn就是a与b的最大公因子?二是该算法的余数最终为什么一定会为0?
现在先来分析第一个问题,通过例子是最容易阐述问题的,下面来看一个例子我们来计算gcd(36, 128):
step 1:用128除以36商3余20,我们记作
128= 3×36 + 20
step 2:取36,用上一步的余数20除36,得
36 =1×20 + 16
step 3:取20,用上一步的余数16除20,得
20 =1×16 + 4
step 4:用4除16求得余数为0,算法结束,即
16 =4×4 + 0
欧几里德算法说明当得到的余数为0时,前一步的余数就是最初两个数(a与b)的最大公因子,即gcd(a, b) = 4.
下面我们来分析一下欧几里德算法的一般情形,如果我们记r0 = a , r1 = b,则
r0 = q1×r1 + r2
r1 = q2×r2 + r3
r2 = q3×r3 + r4
:
ri = qi+1×ri+1 + ri+2 (式 1-1)
:
rn-2= qn-1×rn-1 + rn (rn为最大公因子)
rn-1 = qn×rn + 0
其通项为ri = qi+1×ri+1 + ri+2(i = 0,1,2…)。
现在我们来证明为什么欧几里德算法最后得到的非零余数rn就是a与b的公因数。首先需要证明rn是a与b公因子。从最后一行rn-1 = qn×rn + 0得rn|rn-1(rn整除rn-1),则前一行rn-2 = qn-1×rn-1 + rn,由rn|rn且rn|rn-1得rn|rn-2,同理,逐行上移,当到达第二行r1 = q2×r2 + r3,我们知道rn|r3且rn|r2,故rn|r1= b。最后到达顶行r0 = q1×r1 + r2,利用rn|r2且rn|r1得到rn|r0= a,这就完成了最后非零余数rn是a与b的公因数的证明。现在再证明为什么rn就是a与b最大公因数。假设g是a与b的任意公因数,现在从上往下,从第一行r0 = q1×r1 + r2,由假设g|r0且g|r1,可得g|r2。第二行r1 = q2×r2 + r3,由g|r1且g|r2可得g|r3。同理,逐行往下,我们在每一行中可以得到g整除前两个余数ri和ri+1,于是也得到整除下一个余数ri+2,即g|ri+2。最终到达倒数第二行rn-2 = qn-1×rn-1 + rn,由此我们得到g|rn。由我们的假设g是a与b的任意公因数,而g|rn可得g≤rn,故rn一定是a与b的最大公因数。
第二个问题必须阐述清楚,如果不为0,则会给出不终止的算法。但我们很容易看到,欧几里德算法总会终止,即最后余数肯定会为0。我们每次计算商和余数
A = Q×B + R
余数R都在0与B-1之间。所以,欧几里德算法中的余数不断减小,即
b = r1 > r2 > r3…>rn-1>rn
所有的余数大于等于0,因此得到非负余数是严格递减序列,最终必然会得到等于0的余数。
我们利用拉梅定理来进行算法效率分析。
定理2(拉梅定理) 设a≥b都是正整数,d(b)是b的十进制表示式中数字的个数。若n是用欧几里得算法计算最大公因子(a, b)的步数,则
n≤5d(b)
证明(对数学不感兴趣的网友可以绕过该证明):下面的证明要考虑斐波那契序列
F0 = 0, F1 = 1, Fn = Fn-1 + Fn-2, n ≥ 2
在欧几里得算法的等式中,我们用r0 = a , r1 = b, 使得每个等式有形式
ri = qi+1×ri+1 + ri+2
除了最后一个,即
rn-1 = qn×rn + 0
注意qn≥2:若qn≤1, 则rn-1≤qnrn = rn, 这与rn-1 > rn矛盾.类似地,所有q1,q2,…qn-1≥1:否则存在j≤n-1使得qi = 0且, rj-1 = rj+1,
与严格不等式b = r1 > r2> r3…>rn-1>rn矛盾。
现在
rn≥1 = F2
且由于qn≥2,
rn-1 = qnrn≥2rn≥2F2≥2 = F3
更一般地,让我们对i≥0进行归纳法证明
rn-i ≥ Fi+2
归纳步骤为
rn-i-1 = rn-iqn-i+ rn-i+1
≥rn-i+ rn-i + 1 (因为qn-i≥1)
≥Fi+2 + Fi+1 =Fi+3
所以b = r1 = rn-(n-1) ≥ Fn-1+2 = Fn+1.
为了便于证明,再给出一个斐波那契序列的一个推论
例如d(78) = 2, 所以由拉梅定理保证了计算gcd(326,78)至多需要10步;实际上只有5步。
size_t Euclid(size_t a, size_t b)
{
while (b)
{
size_t r = a % b;
a = b;
b = r;
}
return a;
}
定义3(线性组合) 整数a, b的一个线性组合是指形如sa + tb的整数,其中s, t为整数。
定理3 若a, b是整数,则gcd(a, b)是a, b的一个线性组合。即存在sn,tn,使得
sna+ tnb = (a, b)
这里sn, tn由下面等式递归得到
其中qi为(式1-1)中的不完全商。
如果记r0= a, r1 = b, 注意到rn = (a, b),则我们只需要用数学归纳法证明等式
sia+ tib = ri (i= 0, 1, 2, 3…)
有了上面的递推式,我们很容易得到扩展的欧几里得算法,描述如下:
算法2(扩展的欧几里得算法,Extend Euclid algorithm) 输入两个非负整数a, b,输出三元组{g, s, t},g表示最大公因子, s, t表示a,和b的线性系数,即满足sa +tb = g。
(1) 令r0←a, r1←b, s0←1, s1←0, t0←0, t1←1
(2) if ri= 0, 返回{ri-1,si-1, ti-1}, 结束程序
(3) 令qi←[ri-1/ri], ri+1←ri-1– qiri, si+1←si-1– qisi, ti+1←ti-1– qiti
(4) 返回(2)
由上面的算法描述可以看出扩展的欧几里得算法和欧几里得算法的计算步数是相同的,但扩展的欧几里得算法还能计算出关于(a,b)的一个线性组合。这儿有个疑问可能在为什么通过(式1-2)的递推式就可以得出关于(a, b)的线性组合sna + tnb = (a, b)?
现在用数学归纳法证明sia+ tib = ri (i= 0, 1, 2, 3…):
基础步骤:当i = 0时,s0= 1, t0 = 0, r0 = a, sia + tib = ri成立。
归纳步骤:假设i = n时有sia+ tib = ri成立,有
si+1a+ ti+1b = (si-1- qisi)a + (ti-1 – qiti)b
= si-1 a + ti-1 b –qi (sia + tib)
= ri-1 – qiri (式1-1中有ri = qi+1ri+1+ ri+2)
= ri+1
于是我们证明了
sia+ tib = ri (i= 0, 1, 2, 3…)
//三元组结构体
struct Triple
{
Triple(){}
explicit Triple(int g, int s, int t)
:g(g), s(s), t(t){}
int g; //最大公约数
int s; //a关于g的线性系数
int t; //b关于g的线性系数
};
//扩展的欧几里得算法
Triple EuclidEx(int a, int b)
{
Triple t0(a, 1, 0),
t1(b, 0, 1),
t2;
while (t1.g)
{
int q = t0.g / t1.g;
t2 = Triple(t0.g - q * t1.g,
t0.s - q * t1.s,
t0.t - q * t1.t);
t0 = t1;
t1 = t2;
}
return t0;
}
欧几里得算法很简单,但应用却非常广泛,它所扩展出来的知识也非常多,本文只是简单就其算法本身进行讲解。欧几里得算法是求解线性同余方程的有利工具,如对线性同余方程ax = c (mod p) (其中p为素数),我们可以将线性同余方程改写成ax – py = c, 这和sa + tb= (a, b)是不是很相似呢,的确是这样,我们利用扩展的欧几里得算法就可以方便的求出x和y,特别的,ax = 1(mod p),如果gcd(a, p) = 1,我们可以求出惟一的x,即为a在有限域Fp的逆元,而这在一些加密算法,如RSA,ECC等中应用都十分广泛。
[1][美]Joseph H.Silverman著.数论概论(原书第3版).孙智伟,吴克俭等译.北京.机械工业出版社.2009.6
[2]李继国,余纯武编.信息安全数学基础.湖北.武汉大学出版社.2006.9
[3][美]Joseph J.Rotman著.抽象代数基础教程(原书第3版).李样明,冯明军译.北京.机械工业出版社.2008.1
[4][美]Donald E.Knuth著.The Art of Computer Programming Vol 1:Fundamental Algorithms Third Edition.北京.人民邮电出版社.2012.2