数论-裴蜀定理-扩展欧几里得算法

裴蜀定理

对于任意的整数a、b,都存在一对整数x、y(注意x和y可以是负整数),使得ax+by=gcd(a,b)成立。或者可以这样描述:对方程ax+by=c(a,b,cZ),只有满足gcd(a,b)|c(即a和b的最大公约数可以整除c),方程才有整数解。

裴蜀定理的证明

已知gcd(a,b)=gcd(b,a%b),递归边界就是b=0a=gcd(a,b)。这个时候ax+by=gcd(a,b),只需令x=1,y=0,即可。

从递归边界回归的过程中,当前存在整数x'和y'满足:ax+by=gcd(a,b),再往上一层,则有:a=bb=a%b=aa/bb,代入上式有:

bx+(aa/bb)y=gcd(a,b),即:bx+aya/bby=ay+b(xa/by)=gcd(a,b)

根据ax+by=gcd(a,b) , 得:x=yy=(xa/by)

因为y是整数,x也是整数,而a/b也是向下取整后的整数结果,因此,可以得知:xy也是整数。由此可归纳得证。
以上证明方法,也称为扩展欧几里得算法。

扩展欧几里得算法模板

int exgcd(int a, int b,int &x,int &y){
  if(b==0){		    // 边界
    x = 1, y = 0 ;
    return a;
  }
  int d = exgcd(b,a%b,x,y);
  int k = x;                //K 为 x'
  x = y;		    //x 为 y'
  y = k - a/b*y;            //y 为 x' - a/b*y'
  return d;
}

求解不定方程

对于方程ax+by=c,gcd(a,b)|c ,可以调用exgcd(a,b,x,y),计算出方程ax+by=gcd(a,b)的一组特解(x0,y0)

d=c/gcd(a,b),则ax0d+by0d=gcd(a,b)d=c,故(x0d,y0d)是方程ax+by=c 的一组解

求不定方程ax+by=c 的任意一组解(x,y) ,则有 ax+by=cax+by=c 相减得 a(xx)=b(yy)

得到agcd(a,b)(xx)=bgcd(a,b)(yy)

因为gcd(agcd(a,b),bgcd(a,b))=1,所以agcd(a,b)|yy

故存在整数t,使得 yy=agcd(a,b)t,即 y=yagcd(a,b)t,同理有 x=x+bgcd(a,b)t

x, y 的任意性,方程的全部解都可以表示为 (x+bgcd(a,b)tyagcd(a,b)t) , 其中(x,y)(x0d,y0d)得来,tZ

求x的最小正整数解,设P=bgcd(a,b),那么

x>0时,则x=x%P,相当于t为负整数,让x减少tP,直到最小正整数解,例如当x=11P=3,t=3 时,使x=1133=2 , 相当于 x=11%3=2

x<0时,则x=(x%P+P)%P,相当于t为正整数,让x增加到最小正整数,例如当x=11P=3,t=4 时,使x=11+34=1 , 相当于 x=(11%3+3)%3=(2+3)%3=1

举例:青蛙约会 http://poj.org/problem?id=1061

分析:

设青蛙A和青蛙B跳跃了t次后相遇,则相对于起点0,青蛙A总路程为x+mt ,青蛙B总路程为y+nt ,满足x+mt=y+nt+kl ,kZ

化解方程为(mn)t+kl=yx , 设a=mn,b=l,c=yx , 则有at+bk=c,本题要求t的最小正整数解,若gcd(a,b)c,则说明无解。

利用扩展欧几里得算法实现如下:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll x, y, m, n, l, a, b, c, _x, _y;
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {  // 边界
        x = 1, y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, x, y);
    ll k = x;           // K 为 x'
    x = y;              // x 为 y'
    y = k - a / b * y;  // y 为 x' - a/b*y'
    return d;
}
int main() {
    scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
    a = m - n, b = l, c = y - x;
    if (a < 0) {  //方便计算gcd(a,b)为正整数,a与c一起变号,k为任意整数,故不考虑k
        a = -a, c = -c;
    }
    ll _eg = exgcd(a, b, _x, _y);  //_x可能是负数
    if (c % _eg)
        puts("Impossible");
    else {
        ll d = c / _eg, p = b / _eg;
        _x = _x * d;  //_x不一定是最小正整数解
        if (_x > 0) {
            printf("%lld\n", _x % p);  //相当于_x 减去 t个p 后最小正整数解
        } else {
            printf("%lld\n", (_x % p + p) % p);  //相当于_x 加上 t个p 后最小正整数解
        }
    }
    return 0;
}
posted @   77M  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示