扩展欧几里得是伟大的人类智慧
参考了yoer77文件!!!!
扩展欧几里得定理是OI中经常出现的数论知识。
1.用来解决与线性不定方程的问题
他可以神奇的用来求解二元一次不定方程
形如a*x+b*y=c(a,b均不为0)的方程,a,b,c都是整数,求(x,y)的整数解。
判断是否有解二元一次方程是否存在整数解
扩展欧几里得算法就是在求 a,b 的最大公约数 m=gcd(a,b) 的同时,求出贝祖等式ax + by = m的一个解 (x,y)。
注意此时所解的只是m=gcd(a,b)的情况
整数二元一次不定方程有解的充分必要是gcd(a,b)|c。如果不能整除则无解。
求解整数解
这是扩展欧几里得最难理解也是最精华的部分
贴一份代码
LL exgcd(LL a,LL b,int &xx,int &yy)
{
if(b==0)
{
xx=1;yy=0;
return a;
}
LL r=gcd(b,a%b,xx,yy);
int t=xx;
xx=yy;
yy=t-(a/b)*yy;
return r;
}
我简单谈下我的理解
1.递归求解
什么叫做疯狂递归求解
用类似辗转相除法,求二元一次不定方程47x+30y=1的整数解。
47=30*1+17
30=17*1+13
17=13*1+4
13=4*3+1
然后把它们改写成“余数等于”的形式
17=47*1+30*(-1) //式1
13=30*1+17*(-1) //式2
4=17*1+13*(-1) //式3
1=13*1+4*(-3)
然后把它们“倒回去”
1=13*1+4*(-3) //应用式3
1=13*1+[17*1+13*(-1)]*(-3)
1=13*4+17*(-3) //应用式2
1=[30*1+17*(-1)]4+17(-3)
1=30*4+17*(-7) //应用式1
1=30*4+[47*1+30*(-1)]*(-7)
1=30*11+47*(-7)
得解x=-7, y=11。
用一个方便我自己理解的方式,把求解的过程写出来好了。这里的大括号"{}"里面不是函数体,"{"表示一层递归调用的开始,"}"表示该层递归的结束。
exgcd(47, 30, x, y)
{
r = exgcd(30, 17,x, y)
{
r = exgcd(17, 13, x, y)
{
r = exgcd(13, 4, x, y)
{
r = exgcd(4, 1, x, y)
{
r = exgcd(1, 0, x, y)
{
x = 1;
y = 0;
return 1;
}
t = y = 0;
y = x - (4/1) * y = 1;
x = t = 0;
return r = 1;
}
t = 1;
y = 0 - (13/4) * 1 = -3;
x = 1;
return 1;
}
t = -3;
y = 1 - (17/13) * (-3) = 4;
x = -3;
return 1;
}
t = 4;
y = -3 - (30/17) * 4 = -7;
x = 4;
return 1;
}
t = -7;
y = 4 - (47/30) * (-7) = 11;
x = -7;
return 1;
}
最后的结果:
r = exgcd(47,30,x,y) = 1;
x = -7;
y = 11;
这就可以非常好的理解这个伟大人类智慧
那么r就是最大公约数
x和y就是两个可以是不定方程满足的通解
所以说对于不同的题目我们还要进行不同的对于所得x和y的处理。
2.乘法逆元
乘法的逆元这里只讲用扩展欧几里得的求法
因为根据逆元的定义ax≡1(mod p)且a和p互质
这里求解x就相当于是求gcd(a,p)=1的扩展欧几里得的情况啊这还是相当简单惬意的
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL n,q,x,y;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
LL r=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return r;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
exgcd(i,q,x,y);
while(x<0) x+=q;
printf("%d\n",x);
}
return 0;
}