参考博客:
https://blog.csdn.net/zhjchengfeng5/article/details/7786595
特解来源:
a * x1 + b * y1 = gcd(a,b);
b * x2 + (a%b) * y2 = gcdb,a%b);
a%b可以换成a-(a/b)*b:
a * x1 + b * y1 = b * x2 + (a - (a / b) * b) * y2
整理可得:
a * x1 + b * y1 = a * y2 + b * (x2 - (a / b) * y2);
得出了下面两个等式:
x1 = y2(等式两边a的系数相同)
y1 = x2 - (a / b) * y2 (等式两边b的系数相同)
知道了方程b * x2 + (a%b) * y2 = g(b,a%b) 的解x2, y2,就可以得到方程a * x1 + b * y1 = g(a,b) 的解x1,y1了
运算过程:
a * x + b * y = gcd(a,b),我们可以不断的往下变成b * x + (a % b) y = gcd(a,b)
按照欧几里得的过程,b会变成0,即此时方程a * x + b * y = gcd(a,b) 因为b = 0,变成了a * x = gcd(a,b) ,
还是由欧几里得算法得到,此时a就等于gcd(a,b),所以得到由原方程往下推了不知道多少次的方程 a * x = gcd(a,b) 来说,得到一个解x = 1, y = 0。gcd(a,0)=a;
现在得到了这个方程的解,再回溯回去,
x1 = y2
y1 = x2 - (a / b) * y2
就可以得出原方程 a * x + b * y = gcd(a,b) 的一个特解。
对于关于x,y的方程a * x + b * y = g 来说,让x增加b/g,让y减少a/g,等式两边还相等。(注意一个增加一个减少)
让x增加b/g,对于a * x这一项来说,增加了a * b / g,可以看出这是a,b的最小公倍数
同样,对于b * y这一项来说,y 减少 a/b,这一项增加了a * b / g,同样是a,b的最小公倍数。
可以看出,这两项一项增加了一个最小公倍数,一项减少了一个最小公倍数,加起来的和仍然等于g。
这样我们就明白了,其实这种操作就是增加一个最小公倍数,减少一个最小公倍数,这样来改变x,y的值,来求出所有x,y的通解的。
这时我们让x0不断减 x (x=b/gcd(a,b)) 直到 x0-i*x>=0 && x0-(i+1)*x<0 (i为减x的次数) 这时得到的就是最小整数解
int main()
{
d=exgcd(a,b,x,y);
if(c%d==0)
{
x*=c/d;
t=b/d;
x=(x%t+t)%t; //最小整数解
}
}
a * x1 + b * y1 = g(a,b);
如果c % gcd(a,b) != 0,即c不是gcd的整数倍,则无解。
如果c % gcd(a,b) == 0 且 c / gcd(a,b) = t,那么求出方程 a * x + b * y = gcd(a,b)的所有解x,y,将x,y乘上t,对应的x’,y’即是方程a * x + b * y = t * gcd(a,b)的解
模板1:
void e_gcd(int a, int b, int &gcd, int &x, int &y)
{
if (b == 0)
{
x = 1;
y = 0;
gcd = a;
}
else
{
e_gcd(b, a % b, gcd, y, x); //注意递归位置的对应,递归结束返回时此时&X的值给了y,&y给了x
y -= x * (a / b) //Y1=X2-a/b*y2 变成 y=y1-a/b*x1
}
}
模板2:
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;y=0;
return a;//返回gcd(a,b)
}
ll t=exgcd(b,a%b,x,y);
ll xx=x;//注意交换过程
x=y;
y=xx-(a/b)*y;//注意和上一种的区别
return t;
}
https://vjudge.net/problem/HDU-2669
题意:求解X*a + Y*b = 1,已知a,b,求x和y。典型的扩展欧几里得算法。
注意定义:
long long exgcd(long long a,long long b,long long &x,long long &y)
完整代码:
#include
#include
using namespace std;
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=x*(a/b);
return d;
}
int main()
{
long long a,b,x,y,ans;
while(~scanf("%lld%lld",&a,&b))
{
ans=exgcd(a,b,x,y);
if(ans!=1)
{
puts("sorry");
continue;
}
x=x%b;
if(x<=0)
x+=b;
y=(1-a*x)/b;
printf("%lld %lld\n",x,y);
}
return 0;
}
https://vjudge.net/problem/POJ-2115
题意:
在mod 2^k 范围内,(a+xc)%(2^k) = b 即(a+xc)= y(2^k) + b (x,y均为未知整数)
整理得:xc - y(2^k) = b-a 非常符合拓展欧几里得形式 ax+by=gcd(a,b)
当b-a是gcd(c,2^k))时,有解。
若有解,则利用拓展欧几里得得出特解x0,然后x0要乘上(b-a)/gcd(c,2^k)
令t= (2^k)/gcd(c,2^k);((x0+k*t)*c便是加上一个最小公倍数)
通解x=x0+ k *t,明显当x大于t时,都可以再让k减1
因此最小正整数解x就是x%t==((x0+ k *t)%t)
因为(a+xc)%(2^k) = b
得(a+xc)= y(2^k) + b
得C*X+2^K*Y=B-A
联系ax+by=c=gcd(a,b)
#include
#include
typedef long long ll;
using namespace std;
ll power(ll a,ll b)
{
ll ans=1,base=a;
while(b!=0)
{
if(b%2!=0)
ans=(ans*base);
base=(base*base);
b/=2;
}
return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
ll t=exgcd(b,a%b,x,y);
ll xx=x;
x=y;
y=xx-(a/b)*y;
return t;
}
int main()
{
ll a,b,c,k;
while(~scanf("%lld%lld%lld%lld",&a,&b,&c,&k))
{
ll x,y;
if(a==0&&b==0&&c==0&&k==0)
{
break;
}
ll d=power(2,k);//快速幂!!!
ll gcd=exgcd(c,d,x,y);
if((b-a)%gcd)
printf("FOREVER\n");
else
{
x*=(b-a)/gcd;
x=(x%(d/gcd)+(d/gcd))%(d/gcd);
printf("%lld\n",x);
}
}
return 0;
}