扩展欧几里得与逆元

来源:互联网 发布:ubuntu mysql 数据库 编辑:程序博客网 时间:2024/06/05 17:34

第一次去清北学堂就学了扩欧,当时听得很懵,时隔将近两个月又把课件翻出来琢磨,终于明白了,做一下笔记,写的一塌糊涂不一定都对。

扩展欧几里得算法

众所周知 老师告诉我们,一定存在整数对(x,y)使得ax+by=gcd(a,b)。扩欧就是来求x和y。怎么求呢?既然叫做【扩展】欧几里得,那肯定是跟欧几里得算法有联系了,只不过在原有算法上“稍加修改”…

补一下欧几里得算法的代码:

int gcd(int a,int b){    if(b==0) return a;    else return gcd(b,a%b);}

欧几里得算法的最终状态就是a=gcd(a,b),b=0。有没有恍然大悟?我们只要令x=1,y=0,显然ax+by=gcd(a,b)。那么问题来了,如何从这一个状态往前推出前一个状态呢?

网上有很多推到给出了公式,但我喜欢颓废 我觉得自己感受过程比死板的公式更易理解。我觉得zhw老师举的这个栗子很好的帮我理解了这个问题。

举栗子之前,首先明确一个众所周知的事实:a%b=a-a/b*b

我们来看栗子:

比如12和7,用朴素欧几里得的过程是这样的:
gcd(12,7)=gcd(7,5)=gcd(5,2)=gcd(2,1)=gcd(1,0)=1。

如果我们倒着推:

末状态:1*1+0*0=1

即1*1+0*(2-2*1)=1 -> 0*2+1*1=1

即0*2+1*(5-2*2)=1 -> 1*5-2*2=1

即1*5-2*(7-1*5)=1 -> -2*7+3*5=1

即-2*7+3*(12-1*7)=1 -> 3*12-5*7=1

求得x=3 y=-5

意不意外?激不激动?如果把当前状态设为x1,y1,a,b,后一个状态设为x2,y2,那么前一个状态可以由后一个状态推导得来:x1=y2,y1=x2-a/b*y2。

这样就可以皆大欢喜地改进欧几里得算法了!

int exgcd(int a,int b,int &x,int &y) //直接取x,y的地址,直接修改{    if(b==0)    {        x=1;y=0;        return a;    }    int ans=exgcd(b,a%b,x,y);    //ans存储gcd    int tmp=x;    x=y;    y=tmp-a/b*y;    return ans;}

逆元

如果a*b=1 (mod p),我们就称b是a模p意义下的乘法逆元。

定理:a存在模p的乘法逆元的充要条件是gcd(a,p) = 1。

反证法还是比较好想的,假设gcd(a,p)!=1,那么a*b一定也含有gcd(a,p),就不可能等于1了。

所以我们可以用扩欧求出一组x,y,满足ax+by=1。

可是怎么用扩欧求逆元?看起来多了一项啊。

模掉!(真暴力)

ax+by=1在模b意义下“by”这一项不就没了嘛!

这样,x就可以看做是a模b意义下的乘法逆元。

就可以用扩欧了!

int inver(int a,int p){    int x,y;    int gcd=exgcd(a,p,x,y);    if(gcd!=1) return -1;    //若a,p不互质则无解    int ans=(x%p+p)%p;       //x可能是负的    return ans;}

ps:

在p是质数的情况下求逆元还可以用费马小定理!

费马小定理为: a^(p-1)=1 (mod p) (p为质数)

所以,a的逆元就是a^(p-2) mod p,快速幂即可求出。

原创粉丝点击