扩欧 裴蜀 逆元

引理

  • 如果任意整数a、b不都为0,则gcd(a,b)是集合A={ax+by:x,y∈Z}的最小正元素。

证明:

  • 设s是A中的最小正元素,也就是s=ax′+by′。
  • 设q=⌊a / s⌋,则

a%s=a−qs=a−q(ax+by)=a(1−qx)+b(−qy)

  • 因此,a%s∈A。由于0≤a % s<s,所以a%s=0。同理,b%s=0。因此,s | gcd(a,b),即gcd(a,b)>=s
    同时,因为s∈A,所以gcd(a,b)|s,也就是说gcd(a,b)≤s。得到gcd(a, b)=s。

扩展欧几里得算法.

扩展欧几里得算法(扩O)能在求gcd(a,b)的同时求出丢番图方程ax+by=gcd(a,b)的解。

然而怎么求呢?我们观察gcd(a,b)=gcd(b,a%b),所以设如下两个方程:

ax+by=gcd(a,b)=d
bx′+(a%b)y′=gcd(b,a%b)
明显gcd(a,b)=gcd(b,a%b),也就是ax+by=bx′+(a%b)y′。

为了求得x与y,我们需要保证a,b不变,所以:ax+by=bx′+(a%b)y′=bx′+(a−[a/b]∗b)y′
=ay′+b(x′–[a/b]y′) ([a/b]表示a/b的值向下取整。)

所以可得恒等关系:x = y’ , y = (x’ – [a/b]y’)。一层层往上推,这样就求出了一组特解。

那么如何求全解呢?直接给出递推式:

x=x0+(b/gcd)∗t
y=y0–(a/gcd)∗t
明显,b/gcd与a/gcd是互素的,也就是说它们的gcd为1。如果它们再除以一个值,某些解可能就不是整数了。

裴蜀定理

扩O有什么卵用吗?其实它可以求线性丢番图方程(不定方程)。

对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程(称为裴蜀等式):

ax+by=m
有解当且仅当m是d的倍数。这是由于任何一个a和b的线性组合都被gcd(a, b)整除。

裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数,可用扩O求得。只要将x与y同时乘上 m / gcd(a,b)就行了。

特别来说,方程 ax+by=1有解当且仅当整数a和b互素。(因为 gcd(a,b)=1)

乘法逆元

给定一个形如 ax ≡ 1(%m)的关于x的方程,x即为a关于m的乘法逆元。

怎么求呢?我们把它等价一下:ax%m=1%m=1,ax=m(−y)+1,ax+my=1。
也就是说,只要求出ax+my=1这个方程的解就行了。是不是有点熟悉?对,用扩O+裴蜀求出x,y。

(注意要满足 gcd(a,m)=1)

但是明显,一道题目出给你不可能让你输出无数解,通解也不现实,一般是让你求出x最小的正整数解。那怎么办呢?

x的通解是x0+m/gcd∗t,在乘法逆元情况下gcd=1,所以不用考虑。所以x的通解是x0+mt。

因为通解是x0+mt,明显最小正整数解的区间是,)[0,m),所以似乎只要让x0 % m就可以找到最小解了。

可是万一x0 % m是负数呢?答案x=(x0%b+b)%b。

因为如果x0为负,答案就是b减去x0 % b的绝对值。如果x为正,还要除以b。

说了这么多,理一下思路:扩O能求出ax+by=gcd(a,b)。那么只要整数m=整数∗gcd(a,b),方程就有解,这种方程叫做不定方程。同时根据乘法逆元的性质,只当gcd(a,b)=1时a,b互为乘法逆元。


下面贴代码:

Luogu P1082 同余方程

求关于 x 的同余方程 ax ≡ 1 (% b)的最小正整数解。

#include <cstdio>
using namespace std;
int a, b;
void exgcd(int a, int b, int &x, int &y){
    if (b){
        exgcd(b, a%b, x, y); int tmp=x;
        x=y; y=tmp-(a/b)*y;
    } else x=1, y=0;
    return;
}
int main(){
    scanf("%d%d", &a, &b);
    int x, y;
    exgcd(a, b, x, y);
    x=(x%b+b)%b;
    printf("%d", x);
    return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/9257239.html