欧几里得算法与扩展欧几里得算法
一、欧几里得算法
欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。应用领域有数学和计算机两个方面。计算公式gcd(a,b) = gcd(b,a mod b)。
例:
(1)求解55 22 的最大公约数,则利用欧几里得算法:
gcd(55,22)=gcd(22,55mod22)
=gcd(22,11)
=gcd(11,22mod11)
=gcd(11,0)=11
c代码:
#include<stdio.h> int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } int main(void) { int a,b,min; printf("请输入两个数字\n"); scanf("%d%d",&a,&b); min=gcd(a,b); printf("%d %d 的最大公约数%d\n",a,b,min); system("PAUSE"); return 0; }
扩展欧几里得算法
1、乘法逆元
如果ax≡1 (mod m),且gcd(a,m)=1(a与m互质)
则称 x 是 a 关于 m 的乘法逆元
例:
8 是 7 关于 11 的逆元
因为 7 × 8 = 5 × 11 + 1,所以说 7 模 11 的逆元是 8。
2、扩展欧几里得算法
设a和m是两个整数(至少有一个非零),d = gcd(a,m),则存在整数x和y使得ax + my = d成立。
特殊情况下,如果a和m都是素数,那么存在整数x和y使得ax + my = 1成立。
3、求得a关于m的乘法逆元x
考虑像欧几里得算法一样递归gcd(a,b)=gcd(b,a%b)
假设已知bx’+a%by’=gcd(b,a%b)的一组解x’,y’;
ax+by=gcd(a,b)=gcd(b,a%b)=bx’+(a%b)y’
=bx’+(a - (a/b)* b)* y’
=ay’+ b(x’-(a/b)* y’)
于是得到:x=y’ y=x’- (a/b)* y’
4、那么是否真的存在已知的一组解x’,y’?
首先给出答案,若a,b互素,则存在已知值x’=1,y’=0;
推导过程如下:
由欧几里得算法gcd(a,b) = gcd(b,a mod b)。
若a mod b = 0;则gcd(a,b) = b;
gcd(b,a mod b) = bx’+a%by’ = b1+a%b*0=b 成立
例:求26 关于 15 的乘法逆元
解:gcd (26, 15) = gcd(15, 11) = gcd(11, 4) = gcd(4, 3) = gcd(3, 1) = gcd(1, 0) = 1
故 26 存在 关于15的乘法逆元;
利用关系 于是得到:x=y’ y=x’-(a/b) * y’
逆向求解:
gcd(1, 0)= 1 * 1 + 0 * 0 = 1;此时x = 1,y = 0;
gcd(3, 1)= 3 * 0 + 11 = 1;此时x=0,y=1-(3/1) * 0=1;
gcd(4, 3)= 4*1 + 3 * ( -1) = 1;此时x=1, y=0-(4/3)*1=-1;
gcd(11, 4)= 11 * (-1)+4*3 = 1;此时x= -1,y=1 - (11/4)(-1) = 3;
gcd(15, 11)=153+ 11 (-4) = 1;此时x=3,y=(-1) - (15/11)3 = -4;
gcd (26, 15) = 26 * (-4)+ 15*7= 1;此时x=-4,y= 3- (26 / 15) (-4)= 7;
在密码学算法中,我们需要的逆元一般选择最小正整数;故此时的x=-4,不满足条件;
小技巧:若x<0;x=(x+n)%n;即可;
推导过程如下:
利用模运算性质 (a*b)% n = ((a%n) * (b%n)) % n;
(a *((x+n)%n))%n =( a%n * ((x+n)%n)%n)%n
=(a%n * x%n%n)%n
=(a%n * x%n)%n
=(ax)%n
故满则条件的x应为: -4+15*1 = 11;
求乘法逆元c代码:
#include<stdio.h> int *p;//第1行数组的值 int *q;//第二行数组的值 int h[2][10]; int i; /*欧几里得算法*/ int gcd(int a,int b,int num) { *(p+num)=a; *(q+num)=b; num=num+1; if(b==0) return a; return gcd(b,a%b,num); } //扩展欧几里得求逆元 int vid(int x,int y) { int mid=0; mid=y; //求每次的y值 y=x-(h[0][i]/h[1][i])*y; x=mid; i--; if(i==-1) return x; return vid(x,y); } int main(void) { int a,b,min; int num=0; //用两个指针指向a,b两个数。 p=&h[0][0]; q=&h[1][0]; printf("请输入两个数字\n"); //scanf("%d %d",&a,&b); a=26,b=15; //执行辗转相除法 min=gcd(a,b,num); //判断最大公约数是否为1 if(min==1) printf("%d,%d互素",a,b); else{ printf("%d,%d不互素",a,b); return 0; } //得到数组中最后一个数 for (i = 0; i <10 ; ++i) { //a的值 if(*(p+i)==1) break; } i--; //进入求逆元的递归函数 int x=1,y=0; x=vid(x,y); //判断逆元 if(x>=0) printf("\n逆元为%d",x); else printf("\n逆元为%d ",h[1][0]+x); return 0; }