扩展欧几里得算法
扩展欧几里得算法
扩展欧几里得算法(英语:Extended Euclidean algorithm)是欧几里得算法(又叫辗转相除法)的扩展。已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式
- {\displaystyle ax+by=\gcd(a,b).}
如果a是负数,可以把问题转化成
- {\displaystyle \left|a\right|(-x)+by=\gcd(|a|,b)}({\displaystyle \left|a\right|}为a的绝对值),然后令{\displaystyle x'=(-x)}。
通常谈到最大公约数时,我们都会提到一个非常基本的事实(由裴蜀定理给出):给定二个整数a、b,必存在整数x、y使得ax + by = gcd(a,b)[1]。
众所周知,已知两个数{\displaystyle a}和{\displaystyle b},对它们进行辗转相除(欧几里得算法),可得它们的最大公约数。不过,在欧几里得算法中,我们仅仅利用了每步带余除法所得的余数。扩展欧几里得算法还利用了带余除法所得的商,在辗转相除的同时也能得到裴蜀等式[2](裴蜀定理中描述的等式,裴蜀定理也翻译成贝祖定理)中的x、y两个系数。以扩展欧几里得算法求得的系数是满足裴蜀等式的最简系数。
另外,扩展欧几里得算法是一种自验证算法,最后一步得到的{\displaystyle s_{i+1}}和{\displaystyle t_{i+1}}({\displaystyle s_{i+1}}和{\displaystyle t_{i+1}}的含义见下文)乘以{\displaystyle \gcd(a,b)}后恰为{\displaystyle a}和{\displaystyle b},可以用来验证计算结果是否正确。
扩展欧几里得算法可以用来计算模反元素(也叫模逆元),求出模反元素是RSA加密算法中获得所需公钥、私钥的必要步骤。
算法和举例
在标准的欧几里得算法中,我们记欲求最大公约数的两个数为{\displaystyle a,b},第{\displaystyle i}步带余除法得到的商为{\displaystyle q_{i}},余数为{\displaystyle r_{i+1}},则欧几里得算法可以写成如下形式:
- {\displaystyle {\begin{aligned}r_{0}&=a\\r_{1}&=b\\&\,\,\,\vdots \\r_{i+1}&=r_{i-1}-q_{i}r_{i}\quad {\text{and}}\quad 0\leq r_{i+1}<|r_{i}|\\&\,\,\,\vdots \end{aligned}}}
当某步得到的{\displaystyle r_{i+1}=0}时,计算结束。上一步得到的{\displaystyle r_{i}}即为{\displaystyle a,b}的最大公约数。
扩展欧几里得算法在{\displaystyle q_{i}},{\displaystyle r_{i}}的基础上增加了两组序列,记作{\displaystyle s_{i}}和{\displaystyle t_{i}},并令{\displaystyle s_{0}=1},{\displaystyle s_{1}=0},{\displaystyle t_{0}=0},{\displaystyle t_{1}=1},在欧几里得算法每步计算{\displaystyle r_{i+1}=r_{i-1}-q_{i}r_{i}}之外额外计算{\displaystyle s_{i+1}=s_{i-1}-q_{i}s_{i}}和{\displaystyle t_{i+1}=t_{i-1}-q_{i}t_{i}},亦即:
- {\displaystyle {\begin{aligned}r_{0}&=a&r_{1}&=b\\s_{0}&=1&s_{1}&=0\\t_{0}&=0&t_{1}&=1\\&\,\,\,\vdots &&\,\,\,\vdots \\r_{i+1}&=r_{i-1}-q_{i}r_{i}&{\text{and }}0&\leq r_{i+1}<|r_{i}|\\s_{i+1}&=s_{i-1}-q_{i}s_{i}\\t_{i+1}&=t_{i-1}-q_{i}t_{i}\\&\,\,\,\vdots \end{aligned}}}
算法结束条件与欧几里得算法一致,也是{\displaystyle r_{i+1}=0},此时所得的{\displaystyle s_{i}}和{\displaystyle t_{i}}即满足等式{\displaystyle \gcd(a,b)=r_{i}=as_{i}+bt_{i}}。
下表以{\displaystyle a=240},{\displaystyle b=46}为例演示了扩展欧几里得算法。所得的最大公因数是{\displaystyle 2},所得贝祖等式为{\displaystyle \gcd(240,46)=2=-9*240+47*46}。同时还有自验证等式{\displaystyle |23|*2=46}和{\displaystyle |-120|*2=240}。
序号 i | 商 qi−1 | 余数 ri | si | ti |
---|---|---|---|---|
0 | 240 | 1 | 0 | |
1 | 46 | 0 | 1 | |
2 | 240 ÷ 46 = 5 | 240 − 5 × 46 = 10 | 1 − 5 × 0 = 1 | 0 − 5 × 1 = −5 |
3 | 46 ÷ 10 = 4 | 46 − 4 × 10 = 6 | 0 − 4 × 1 = −4 | 1 − 4 × −5 = 21 |
4 | 10 ÷ 6 = 1 | 10 − 1 × 6 = 4 | 1 − 1 × −4 = 5 | −5 − 1 × 21 = −26 |
5 | 6 ÷ 4 = 1 | 6 − 1 × 4 = 2 | −4 − 1 × 5 = −9 | 21 − 1 × −26 = 47 |
6 | 4 ÷ 2 = 2 | 4 − 2 × 2 = 0 | 5 − 2 × −9 = 23 | −26 − 2 × 47 = −120 |
证明
由于{\displaystyle 0\leq r_{i+1}<|r_{i}|},{\displaystyle r_{i}}序列是一个递减序列,所以本算法可以在有限步内终止。又因为{\displaystyle r_{i+1}=r_{i-1}-r_{i}q_{i}}, {\displaystyle (r_{i-1},r_{i})}和{\displaystyle (r_{i},r_{i+1})}的最大公约数是一样的,所以最终得到的{\displaystyle r_{k}}是{\displaystyle a},{\displaystyle b}的最大公约数。
在欧几里得算法正确性的基础上,又对于{\displaystyle a=r_{0}}和{\displaystyle b=r_{1}}有等式{\displaystyle as_{i}+bt_{i}=r_{i}}成立(i = 0 或 1)。这一关系由下列递推式对所有{\displaystyle i>1}成立:
- {\displaystyle r_{i+1}=r_{i-1}-r_{i}q_{i}=(as_{i-1}+bt_{i-1})-(as_{i}+bt_{i})q_{i}=(as_{i-1}-as_{i}q_{i})+(bt_{i-1}-bt_{i}q_{i})=as_{i+1}+bt_{i+1}}
因此{\displaystyle s_{i}}和{\displaystyle t_{i}}满足裴蜀等式,这就证明了扩展欧几里得算法的正确性。
实现
以下是扩展欧几里德算法的Python实现:
def ext_euclid(a, b): old_s,s=1,0 old_t,t=0,1 old_r,r=a,b if b == 0: return 1, 0, a else: while(r!=0): q=old_r//r old_r,r=r,old_r-q*r old_s,s=s,old_s-q*s old_t,t=t,old_t-q*t return old_s, old_t, old_r
扩展欧几里得算法C语言实现:
#include <stdio.h> //这里用了int类型,所支持的整数范围较小且本程序仅支持非负整数,可能缺乏实际用途,仅供演示。 struct EX_GCD { //s,t是裴蜀等式中的系数,gcd是a,b的最大公约数 int s; int t; int gcd; }; struct EX_GCD extended_euclidean(int a, int b) { struct EX_GCD ex_gcd; if (b == 0) { //b等于0时直接结束求解 ex_gcd.s = 1; ex_gcd.t = 0; ex_gcd.gcd = 0; return ex_gcd; } int old_r = a, r = b; int old_s = 1, s = 0; int old_t = 0, t = 1; while (r != 0) { //按扩展欧几里得算法进行循环 int q = old_r / r; int temp = old_r; old_r = r; r = temp - q * r; temp = old_s; old_s = s; s = temp - q * s; temp = old_t; old_t = t; t = temp - q * t; } ex_gcd.s = old_s; ex_gcd.t = old_t; ex_gcd.gcd = old_r; return ex_gcd; } int main(void) { int a, b; printf("Please input two integers divided by a space.\n"); scanf("%d%d", &a, &b); if (a < b) { //如果a小于b,则交换a和b以便后续求解 int temp = a; a = b; b = temp; } struct EX_GCD solution = extended_euclidean(a, b); printf("%d*%d+%d*%d=%d\n", solution.s, a, solution.t, b, solution.gcd); printf("Press any key to exit.\n"); getchar(); getchar(); return 0; }