扩展欧几里得算法_逆元算法

f993b5370a3ae5175ad671f4c57fe092.gif

逆元:

概念:

若ab≡1 (mod m),则称a与b在模m的情况下互为逆元。记b=a,所以b又叫a的数论倒数。

我们介绍两种方法求解逆元:

1.费马小定理:

假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

逆元:对于a和p,若a*b%p≡1,则称b为a%p的逆元。

除法的逆元,即求(a/b)%p的值

通常情况下,p均为质数,则公约数为1的情况基本都可以保证

由费马小定理得:

b^(p-1)%p=1 则:

b*b^(p-2)%p=1 两边同乘a/b,然后左右式交换得:

a/b=a/b*b*b^(p-2)%p 化简得:

a/b=a*b^(p-2)%p

此时的结果即为a/b的结果,取模得(a/b)%p;

2.扩展欧几里得

为了介绍扩展欧几里得,我们先介绍一下贝祖定理:

即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。

换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)

有一个直接的应用就是 如果ax+by=1有解,那么gcd(a,b)=1;

要求出这个最大公因数gcd(a,b),我们最容易想到的就是古老悠久而又相当强大的辗转相除法:

int gcd(int a,int b){    return b==0?a:gcd(b,a%b);}

但是,对于上面的式子ax+by=m来说,我们并不仅仅想要知道有没有解,而是想要知道在有解的情况下这个解到底是多少。

所以,扩展欧几里得

        当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a*1+b*0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。

        初步想法:由于是递归的算法,如果我们知道了这一层和上一层的关系,一层一层推下去,就可以推到最开始的。类似数学上的数学归纳法。

        假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得b*x1+(a%b)*y1=gcd

(注意在递归算法中,永远都是先得到下面一个状态的值)

这时我们可以试着去寻找这两个相邻状态的关系:

首先我们知道:a%b=a-(a/b)*b;带入:

b*x1 + (a-(a/b)*b)*y1

= b*x1 + a*y1 – (a/b)*b*y1

= a*y1 + b*(x1 – a/b*y1) = gcd   

发现 x = y1 , y = x1 – a/b*y1

这样我们就得到了每两个相邻状态的x和y的转化,就可以在求gcd的同时对x和y进行求值了

接下来我们重点用代码介绍扩展欧几里得求逆元。

首先引入快速幂与快速乘

ll qmul(ll a,ll b,ll m)                     //快速乘{  ll ans=0;  a%=m;  b%=m;  while(b)  {    if(b&1)    {      ans=(ans+a)%m;    }    a=(a+a)%m;    b>>=1;  }  return ans;}
ll fast_pow(ll x, ll k, ll p){                    //快速幂    ll ret=1;    x%=p;    while(k>0){        if(k&1){            ret= qmul(ret, x, p);        }        k>>=1;        x= qmul(x, x, p);    }    return ret;}
void extgcd(ll a,ll b,ll& d,ll& x,ll& y)            //拓展欧几里得{    if(!b)    {        d=a;        x=1;        y=0;    }    else    {        extgcd(b,a%b,d,y,x);        y-=x*(a/b);    }}ll ModularInverse(ll a,ll b){    ll d,x,y;    extgcd(a,b,d,x,y);    return d==1?(x+b)%b:-1;  //返回的结果就是(1/a)mod(b)的结果    // complete this part}

引入一道例题:蓝桥杯2019A组省赛E题

0cf65e68cc20aa576584766866350593.png

附解题代码:

#includeusing namespace std;typedef long long ;qq fast_mul(qq x, qq y, qq p){    qq ret=0;    x%=p, y%=p;    while(y>0){        if(y&1){            ret= (ret+x)%p;        }        y>>=1;        x= (x+x)%p;    }    return ret;}qq fast_pow(qq x, qq k, qq p){    qq ret=1;    x%=p;    while(k>0){        if(k&1){            ret= fast_mul(ret, x, p);        }        k>>=1;        x= fast_mul(x, x, p);    }    return ret;}qq phi(qq n){    qq ret= n;    for(int i=2;i*i<=n;i++){        if(n%i==0){            ret= ret/i*(i-1);            while(n%i==0) n/=i;        }    }    if(n!=1){        ret= ret/n*(n-1);    }    return ret;}qq get_p(qq n){    for(qq i=2;i<=n;i++){        if(n%i==0){            return i;        }    }}int main(){    qq n = (qq)1001733993063167141;    qq d = 212353;    qq C = 20190324;    qq p,q,e,k;    printf("n=%lld\n",n);    p=get_p(n);    q=n/p;    printf("p=%lld, q=%lld\n",p,q);    k=(p-1)*(q-1);    printf("k=(p-1)*(q-1)=%lld\n",k);    printf("phi(k)=%lld\n",phi(k));    e=fast_pow(d,phi(k)-1,k);    printf("e=d^(phi(k)-1)=%lld (mod k)\n",e);    printf("d*e=%lld (mod k)\n",fast_mul(d,e,k));    qq X=fast_pow(C,e,n);    printf("X=C^e (mod n)= %lld\n",X);    while(1)getchar();    return 0;}

运行结果如下:

cf51a8f417c0e3f4a4a6cddf1a32a502.png

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
扩展欧几里得算法解一元一次不定方程 ax + by = gcd(a,b) 的一种方法,其中 a 和 b 是整数,gcd(a,b) 是它们的最大公约数,x 和 y 是整数解。逆元是指在模运算下,一个数的乘法逆元是指与它相乘后模运算得到 1 的数。在数论中,常常需要一个数在模意义下的逆元,即一个数 k 满足 (k * x) % m = 1,其中 m 是模数。 下面是扩展欧几里得算法逆元的 C 语言实现: ```c #include <stdio.h> // 扩展欧几里得算法 int exgcd(int a, int b, int *x, int *y) { if (b == 0) { *x = 1; *y = 0; return a; } int gcd = exgcd(b, a % b, y, x); *y -= a / b * (*x); return gcd; } // 逆元 int modinv(int a, int m) { int x, y; int gcd = exgcd(a, m, &x, &y); if (gcd != 1) { return -1; // a 和 m 不互质,不存在逆元 } else { return (x % m + m) % m; // 转化为正整数 } } int main() { int a = 3, m = 11; int inv = modinv(a, m); if (inv == -1) { printf("%d 在模 %d 意义下不存在逆元\n", a, m); } else { printf("%d 在模 %d 意义下的逆元是 %d\n", a, m, inv); } return 0; } ``` 这个程序中,exgcd 函数通过递归实现扩展欧几里得算法,返回 a 和 b 的最大公约数,并且出 x 和 y 的值。在 modinv 函数中,我们调用 exgcd 函数出 a 和 m 的最大公约数,并且判断 a 和 m 是否互质,如果不互质则不存在逆元。否则,根据扩展欧几里得算法的结果,出 x 的值作为 a 在模 m 意义下的逆元。注意,由于 x 可能是负数,所以要将其转化为正整数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值