扩展欧几里得学习总结

扩展欧几里得算法

     用来在已知整数a,b的情况下求解符合条件的x,y值,满足等式ax+by=gcd(a,b)。
     且有对于整数 a,b, 必然存在整数对 x,y,满足ax+by=gcd(a,b)。

有关算法的递归思想

     由欧几里得算法可知求解两个整数gcd的过程,即gcd(a,b)=gcd(b,a%b)。那么当辗转到最终状态时b=0,a=gcd,此时对于ax+by=gcd(a,b)对应着x=1,y=0,即a*1+0*y=gcd(a,b) (其实此时y的取值是无关紧要的,毕竟y的系数已经化为0)。当然这只是最终状态下x,y的取值,那么我们应该怎样由最终的状态反推到之前的状态呢
假设我们当前已经进入gcd(b,a%b)的化简状态,则有b*x+(a%b)*y=gcd,
可化简为 b*x+(a-(a/b)*b)*y=gcd,(a/b为下取整)
继续化简 b*x+a*y-(a/b)*b*y=gcd,
则可得 a*y+b*(x-(a/b)*y)=gcd
对比于之前的等式 ax+by=gcd 我们不难发现 x 与 y 之间的变换关系,即x=y,y=x-(a/b)*y,由此关系进行递归即可。

有关解的增长周期关系

     对于不定方程ax+by=gcd(a,b),肯定是存在多组解的,使用扩展欧几里得算法处理得到x0,y0的只是一组特解,那么我们应该怎样用x0,y0去表示通解从而方便我们求得符合题意要求的解呢
     ax+by=gcd 可化简为 y= bax+gcd ,由该方程我们可知,x 每增加 m,y 值便减少 bam ,一方面因为我们需要求解整数解,所以 m 和 bam 必须是整数,另一方面我们需要尽可能的表示出 x,y 解的最大范围,所以 m 必须取尽可能小的值,其对应的 bam 也相应最小。所以可得 m= bgcd ,此时x和y的增长周期分别为 bgcd agcd ,两个数互为素数,满足最小整数值的要求。则有通解 x=x0+bgcdy=y0agcd

算法应用

1、求解不定方程
     对于方程ax+by=c方程的求解,只需要由上述概述加上一步的转化
     即在ax+by=gcd的等式两端同时乘上 cgcd ,那么对于ax+by=gcd的特解x0也需要乘上 cgcd 才是ax+by=c方程的特解, 所以当c%gcd!=0时,是属于无解情况,关于通解的表示与上述还是一样的。
POJ 2115 C Looooops

2、求解模线性方程(线性同余方程)
(1)一元线性同余方程组
     以POJ 2891为例
     先由两个方程说起, 对于M%a1=r1       M%a2=r2两个关系式可以转化为
     a1*x+r1=M       a2*x+r2=M
     两式相减便有:a1*x-a2*y=r2-r1
     对于该方程使用扩展欧几里得算法可以得到一个特解x0,则X可由一个满足这两个方程的值,即x=x0*a1+r1。
     且X的通解可以表示为:X=x+k*lcm(a1,a2)
     即 lcm(a1,a2)*z+x=X 现在则实现了将两个方程合并,则合并后新的方程的 a1’=lcm(a1,a2)=a1(a2/gcd),r1’=x=x0*a1+r1。
以此类推,与剩下的n-2个方程进行合并求出最终解。

代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll __int64
int n;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){
        x=1,y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ans;
}
void solve()
{
    ll a1,r1,a2,r2,a,b,c,t;
    ll d,x0,y0;
    bool flag=1;
    scanf("%lld %lld",&a1,&r1);
    for(int i=1; i<n; i++)
    {
        scanf("%lld %lld",&a2,&r2);
        a=a1,b=a2,c=r2-r1;
        d=exgcd(a,b,x0,y0);
        if(c%d!=0)
        {
            flag=0;
        }
        ll t=b/d;
        x0=(x0*(c/d)%t+t)%t;
        r1=a1*x0+r1;
        a1=a2/d*a1;
        r1%=a1;
    }
    if(!flag)
    {
        printf("-1\n");
        return ;
    }
    if(r1<0)
        r1+=a1;
    printf("%lld\n",r1);
}

int main()
{
    while(~scanf("%d",&n))
        solve();
    return 0;
}

(2)多元线性同余方程组
     后序补更……
3、求解模的逆元
     有关乘法逆元,满足a*x%m=1,即ax+my=1,即为求解不定方程c=1的情况
     题目中一般会要求输出最小的正整数,因为最后结果是对 m 取模,所以当题意中的 m 有可能是负数时,要先对 m 取绝对值,且通解可以表示为 X=x0+(m/gcd)*t=x0+m*t(gcd必为1,否则c%gcd!=0无解)。当x0是非正数时,对 m 取模仍然是非正数,再加上 m 即可。

ZOJ 3609 Modular Inverse

#include <iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
ll d;
ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        d=a,x=1,y=0;
    }
    else
    {
        ex_gcd(b,a%b,y,x);
        y-=(a/b)*x;
    }
    return d;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        ll a,m,x,y;
        scanf("%lld %lld",&a,&m);
        ll gcd=ex_gcd(a,m,x,y);
        if(gcd!=1)
            printf("Not Exist\n");
        else
        {
            m=abs(m);
            x%=m;
            if(x<=0)
                x+=m;
            printf("%lld\n",x);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值