欧几里得算法与扩展欧几里得算法

一、欧几里得算法

欧几里得算法又称辗转相除法,是指用于计算两个非负整数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;
}

 

posted @ 2022-03-31 21:06  丹青初鸿  阅读(217)  评论(0编辑  收藏  举报