扩展欧几里得算法
一、问题的提出
首先介绍一下贝祖定理: 即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
同理可推出:如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。
扩展欧几里得算法不仅要求出a,b的最大公因数,还要求出ax+by=gcd(a,b)的解。
扩展的欧几里得算法有什么用呢?
- 求解不定方程
- 求解模线性方程(线性同余方程)
- 求解模的逆元
二、求解算法(递推算法为例)
r0=a, r1=b; s0=1, s1=0; t0=0, t1=1;
在最终的式子中,只有r0, r1, s0, s1, t0, t1可以保留。
令:r0=s0 * a+t0 * b;
r1=s1 * a+t1 * b;
通过数学归纳法可以得到下列形式成立:rj=sj * a+tj * b;
辗转相除式 | 变形 | 带入上一式数值 | 规律 |
---|---|---|---|
r0=r1* q1+r2; | r2=r0-r1 * q1; | r2=s0 * a+t0 * b-r1 * (s1* a+t1* b) =(s0-r1 * s1) *a+(t0-r1 * t1) * b; | #s2=s0-q1 * s1, t2=t0-q1 * t1# |
r1=r2 * q2+r3; | r3=r1-r2 * q2; | r3=(s1* a+t1 * b)-(s2 * a+t2 * b) * q2 =(s1-s2 * q2) *a+(t1-t2 * q2) * b | #s3=s1-q2 * s2, t3=t1-q2 * t2# |
… | |||
rj-1=rj * qj+rj+1; | rj+1=rj-1-rj * qj; | #now, rj+1=0, rj=gcd(a,b)# |
可以发现递推规律:
sj+1=sj-1-qj * sj;
tj+1=tj-1-qj * tj;
当rj+1为0时,递推终止。此时rj为最大公因子。sj 与 tj为s * a+t * b= gcd(a,b)的解。
C语言代码如下:(可能与递归算法相形见绌,但是也不失为一种较好的算法)
#include<stdio.h>
int main()
{
int r0=0,r1=0,r2=100;
int q0=0,q1=0;
int s0=0,s1=0,s2=100;
int t0=0,t1=0,t2=100; //0代表j-1,1代表j,2代表j+1(为了符合变量命名的规范...)
int a=0,b=0;
scanf("%d%d",&a,&b);
while(a<=b)
{
printf("Warning: variable 'a' must larger than 'b'\n");
printf("Please input again\n");
scanf("%d%d",&a,&b);
} //警告:输入的a必须大于b,否则重新输入
r0=a; r1=b;
s0=1; s1=0;
t0=0; t1=1; //最终保留的变量的初始化
printf("\nq0 q1 r0 r1 s0 s1 t0 t1\n");
//这是为了输出每一步的递推结果,方便查看,可有可无
while(r2!=0) //当r2为0时,已经得到最大公因子r1
{
q1=r0/r1;
r2=r0-r1*q1; s2=s0-q1*s1; t2=t0-q1*t1; //递推公式
t0=t1; t1=t2; //以下三行均将j的值传给j-1,j+1的值传给j
s0=s1; s1=s2;
r0=r1; r1=r2;
printf("%-8d%-8d%-8d%-8d%-8d%-8d%-8d%-8d\n",q0,q1,r0,r1,s0,s1,t0,t1);
}
printf("\nresult: a= %d b= %d gcd(a,b)= %d\n",s0,t0,r0);
//最后得到的结果是已经经历了传递过程的,所以j-1的值才是结果
printf("%d= %d* %d+ %d* %d",r0,s0,a,t0,b);
return 0;
}
当然,除了这种递推的方法,还有递归等算法,在此不加赘述。
三、扩展欧几里得算法应用
1、求解不定方程
这个就很显然嘛,一直都是在求解不定方程……
2、求解模线性方程
3、求解模的逆元
逆元的定义:如果a * x ≡ 1(mod p),且gcd(a , p)=1,称x为a关于模p的乘法逆元。
上面的表述等价于:x *a+y *p=1有解。我们可以用扩展的欧几里得算法求解出一组特殊解x‘ ,然后令 x = x’ % p, 得到逆元。(若s为负数则可取绝对值,符合某性质)