扩展欧几里得算法就是通过欧几里得算法在求最大公约数的时候求解不定方程
欧几里得定理:gcd(a, b)=gcd(b,a%b)
求解不定方程:a*x + b*y = gcd
通解:
x=x0+(b/gcd)*t
y=y0-(a/gcd)*t
例子
#include<iostream>
#include<cstdio>
using namespace std;
int egcd(int a,int b,int &x,int&y) //扩展欧几里得算法
{
if(b==0)
{
x=1,y=0; //初始化
return a;
}
int ans=egcd(b,a%b,x,y);
int temp=x; //调整系数
x=y;
y=temp-a/b*y;
return ans;
}
int main()
{
int a,b,c,x,y,gcd;
while(cin>>a>>b>>c) //输出a*x+b*y=c
{
gcd=egcd(a,b,x,y);
if(c%gcd) //方程有解的充要条件
printf("not exist\n");
else
{
x*=c/gcd,y*=c/gcd;
printf("(%d*%d)+(%d*%d)=%d\n",a,x,b,y,c); //输出其中一组解
b/=gcd;
if(b<0)b=-b; //如果mod值小于0,取绝对值
x%=b;
if(x<0)x+=b; //x值小于0,加上mod值
printf("%d\n",x); //输出最小的正x
}
}
return 0;
}
#include<iostream>
#include<cstdio>
using namespace std;
void egcd(int a,int b,int&d,int&x,int&y) //扩展欧几里得算法
{
if(!b)d=a,x=1,y=0; //初始化
else
{
egcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
int main()
{
int a,b,c,x,y,gcd;
while(cin>>a>>b>>c) //输出a*x+b*y=c
{
egcd(a,b,gcd,x,y);
if(c%gcd) //方程有解的充要条件
printf("not exist\n");
else
{
x*=c/gcd,y*=c/gcd;
printf("(%d*%d)+(%d*%d)=%d\n",a,x,b,y,c); //输出其中一组解
b/=gcd;
if(b<0)b=-b; //如果mod值小于0,取绝对值
x%=b;
if(x<0)x+=b; //x值小于0,加上mod值
printf("%d\n",x); //输出最小的正x
}
}
return 0;
}
迭代实现
int exgcd(int m,int n,int &x,int &y)
{
int x1,y1,x0,y0;
x0=1; y0=0;
x1=0; y1=1;
x=0; y=1;
int r=m%n;
int q=(m-r)/n;
while(r)
{
x=x0-q*x1; y=y0-q*y1;
x0=x1; y0=y1;
x1=x; y1=y;
m=n; n=r; r=m%n;
q=(m-r)/n;
}
return n;
}
扩展欧几里得算法应用
(1)求解不定方程
用扩展欧几里得算法解不定方程ax+by=c;
这个应该比较好理解了,两个可以同乘以k
bool linear_equation(int a,int b,int c,int &x,int &y)
{
int d=exgcd(a,b,x,y);
if(c%d)
return false;
int k=c/d;
x*=k; y*=k; //求得的只是其中一组解
return true;
}
(2)求解模线性方程(线性同余方程)
同余方程 ax≡b (mod n) (也就是 ax % n = b) 对于未知数 x 有解,当且仅当 gcd(a,n) | b (也就是 b % (gcd(a,n))==0 )。且方程有解时,方程有 gcd(a,n) 个解。
求解方程 ax≡b (mod n) 相当于求解方程 ax+ ny= b, (x, y为整数)
例子:
在方程 3x ≡ 2 (mod 6) 中, d = gcd(3,6) = 3 ,3 不整除 2,因此方程无解。
在方程 5x ≡ 2 (mod 6) 中, d = gcd(5,6) = 1,1 整除 2,因此方程在{0,1,2,3,4,5} 中恰有一个解: x=4。
证明略去,直接说算法:
首先看一个简单的例子:
5x=4(mod3)
解得x = 2,5,8,11,14…….
由此可以发现一个规律,就是解的间隔是3.
那么这个解的间隔是怎么决定的呢?
如果可以设法找到第一个解,并且求出解之间的间隔,那么就可以求出模的线性方程的解集了.
我们设解之间的间隔为dx.
那么有
a*x = b(mod n);
a*(x+dx) = b(mod n);
两式相减,得到:
a*dx(mod n)= 0;
也就是说a*dx就是a的倍数,同时也是n的倍数,即a*dx是a 和 n的公倍数.为了求出dx,我们应该求出a 和 n的最小公倍数,此时对应的dx是最小的.
设a 和 n的最大公约数为d,那么a 和 n 的最小公倍数为(a*n)/d.
即a*dx = a*n/d;
所以dx = n/d. (d = gcd(a,n) )
因此解之间的间隔就求出来了.
bool modular_linear_equation(int a,int b,int n)
{
int x,y,x0,i;
int d=exgcd(a,n,x,y);
if(b%d)
return false;
x0=x*(b/d)%n; //特解
for(i=1;i<d;i++)
printf("%d\n",(x0+i*(n/d))%n);
return true;
}
(3)求解模的逆元;
同余方程ax≡b (mod n),如果 gcd(a,n)== 1,则方程只有唯一解。
在这种情况下,如果 b== 1,同余方程就是 ax=1 (mod n ),gcd(a,n)= 1。
这时称求出的 x 为 a 的对模 n 乘法的逆元。
对于同余方程 ax= 1(mod n ), gcd(a,n)= 1 的求解就是求解方程
ax+ ny= 1,x, y 为整数。这个可用扩展欧几里德算法求出,原同余方程的唯一解就是用扩展欧几里德算法得出的 x 。