拓展欧几里得 入门总结

参考博客:

https://blog.csdn.net/zhjchengfeng5/article/details/7786595

理解拓展欧几里得

求ax+by=c的解(c一定是gcd(a,b)的倍数)否则无解

1.求特解

特解来源:

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) 的一个特解。

 

2.利用特解推出其他所有整数解


对于关于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;
}

例题:

1.Romantic

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<stdio.h>
#include<algorithm>
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;


}

2.C Looooops

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<stdio.h>
#include<algorithm>
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;

}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值