额…晚上听学长讲了扩展欧几里得,听得迷迷糊糊的,还是要怪自己没有提前去预习,甚至不知道这个算法能干啥…学长讲完之后自己去翻了将近两个小时的博客才稍微能理解一点。
扩展欧几里得
欧几里得算法gcd(a,b)可以算出a,b的最大公因数(后面就把这个最大公因数用g来表示吧),扩展欧几里得就是在算出g的同时算出x,y满足:
ax+by=g
这里其实有个贝祖定理:
即如果a、b是整数,那么一定存在整数x、y使得ax+by=g。
换句话说,如果ax+by=m有解,那么m一定是g的若干倍。(可以来判断一个这样的式子有没有解)
先看看欧几里得的代码
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
上面代码可以求出g,我们不难发现在递归结束时
b=0,a=g,且此时满足 ax+by=g,所以可以得出x=1,y=0
但是这时的a,b已经不是开始的那个a,b了,所以我们需要回到最初的模样(突然非主流哈哈哈哈哈)。
因为我求g用的是递归,所以我们如果知道这一层与下一层x,y与x1,y1的关系,就能一层层的推回原来的a,b所对应的x,y。
假设我们已经递归到了下一个状态
b*x1+(a%b)*y1=gcd
首先我们要知道a%b=a-(a/b)*b(不理解的话找两个数代入就懂了)代入上式可得
b*x1 + (a-(a/b)*b)*y1
= bx1 + ay1 – (a/b)by1
= ay1 + b(x1 – a/b*y1) = gcd
可以发现 x=y1,y=x1-a/b*y1
既然找到了关系那么就能在求g的同时求出x,y了
int ex_gcd( int a, int b,int& x ,int& y ){
if( b == 0){
x = 1;
y = 0;
return a;//递归到边界,开始返回上一层
}
int g = ex_gcd( b,a%b ,x,y);
int t = x;
x = y;
y = t - a/b * y;
return g;
}
int main(void){
int a,b,g,x,y;
scanf("%d%d",&a,&b);
g = ex_gcd(a,b,x,y);
printf("%d * %d + %d * %d = %d",a,x,b,y,g);
return 0;
}
同余方程(求逆元)
什么是逆元呢?
a*x≡1(mod b)
即a乘x对b取模等于1,那么a与x关于b互为乘法逆元
对于整数a,b只有当gcd(a,b)=1时才能求a对b的乘法逆元
ax ≡ 1 (mod b) 可以写成 ax-bk=1,观察上面的拓欧ax+by=gcd(a,b)
当gcd(a,b)=1时有ax+b*y=1
所以我们只需令k = -y,就可以用扩欧来求逆元了
用这种方法求出来的x,可能不是最小正整数,
我们只需让x=(x%b+b)%b,即可求出最小的正逆元了