欧几里得算法、扩展欧几里得与同余方程

欧几里得与最大公因数

信竞里最常用的 \(\gcd\) 算法就是欧几里得算法,这是我学会的第一个会写代码但不会算时间复杂度的算法

欧几里得算法的基本定理:对于任意两个数 \(a,b\) ,如果 \(a>b\) ,那么 \(\gcd (a,b) = \gcd(b,a \bmod b)\)

由于这个定理简单易懂,读者自证不难

所以这里显然可以用递归的方法来计算 \(\gcd\)

代码自己去看 最大公因数模板

这里顺便计算一下欧几里得算法的时间复杂度

如果令 \(a,b\leq n\)

根据以上代码,递归分为两种情况:

  • \(a<b\),这时候,\(\gcd(a,b)=\gcd(b,a)\)
  • \(a \geq b\),这时候,\(\gcd(a,b)=\gcd(b,a \bmod b)\) ,可以得到 \(a \bmod b \leq \dfrac{a}{2}\),所以这种情况最多发生 \(2 \log n\)

由于第一情况发生后必然发生第二情况,所以第一情况发生次数必然不大于第二情况发生次数,所以整体递归次数不超过 \(4 \log n\),所以这一算法时间复杂度最坏为 \(O(\log n)\)

扩展欧几里得

这里请出这篇学习笔记的主角——扩展欧几里得算法

扩展欧几里得,根据名字就可以想到,是欧几里得算法的扩展加强版

考虑一道例题:对于三个自然数 \(a,b,c\),求出 \(ax+by=c\) 的整数解

将方程两边同时除以 \(\gcd(a,b)\)\(\dfrac{a}{\gcd(a,b)}x+\dfrac{b}{\gcd(a,b)}y=\dfrac{c}{\gcd(a,b)}\)

由于 \(\dfrac{a}{\gcd(a,b)}x+\dfrac{b}{\gcd(a,b)}y\) 是一个整数,所以 \(\dfrac{c}{\gcd(a,b)}\) 的值也是整数

\(\gcd(a,b)|c\)

所以这个方程要想有整数解,必须满足 \(\gcd(a,b)|c\)

所以先计算 \(\gcd\),然后判断有没有解

考虑这组方程的特殊情况,也就是在 \(c=\gcd(a,b)\) 时,方程变为 \(ax+by=\gcd(a,b)\)

然后用欧几里得基本定理来推一波式子:

\[\because ax+by=\gcd(a,b)=\gcd(b,a \mod b), bx+(a \mod b)y=gcd(b,a\mod b)\\ \therefore bx+(a \bmod b)y=gcd(a , b)\\ \therefore bx+(a-\lfloor \dfrac{a}{b} \rfloor b)y=\gcd(a,b)\\ \therefore ay+b(x-\lfloor \dfrac{a}{b} \rfloor y)=\gcd(a,b) \]

于是就得出了扩展欧几里得基本定理:

\(ax+by=\gcd(a,b)\)\(ay+b(x-\lfloor \dfrac{a}{b} \rfloor y)=\gcd(a,b)\) 的整数解一样

void exgcd(int a,int b,int &x,int &y)
{
    if(!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}

于是乎,扩展欧几里得就完成了,但这里求的是 \(x_0,y_0\),也就是特殊情况

如何求出 \(ax+by=c\) 的解呢?

\(k=\dfrac{c}{\gcd(a,b)}\)

\(ax_0+by_0=\gcd(a,b)\) 同时乘以 \(k\)

则方程变为 \(akx_0+bky_0=k\gcd(a,b)\)

化简为 \(akx_0+bky_0=c\)

\(x=kx_0,y=ky_0\)

则全部代码如下:

void exgcd(int a,int b,int &x,int &y)
{
    if(!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}

inline int gcd(int a,int b)
{
    if(!a) return b;
    if(!b) return a;
    while(b^=a^=b^=a%=b);
    return a;
}

bool answer(int a,int b,int c,int& x,int& y)
{
    int d=gcd(a,b);
    if(c%d) return 0;
    int k=c/d;
    exgcd(a,b,x,y);
    x*=k,y*=k;
    return 1;
}

与欧几里得算法同理,扩展欧几里得的复杂度也是 \(O(\log n)\)

具体证明可以参照欧几里得算法的时间复杂度计算

同余方程

最后的最后,是关于同余方程的部分

是不是很奇怪?为什么会跟同余方程有关系?

将上文中所考虑的 \(ax+by=c\) 的方程两边同时模上 \(b\) ,则整个方程变为 \(ax \equiv c \pmod b\)

就是一个标准的同余方程

于是乎,\(ax+by=c\) 与同余方程是完全等价的

所以用扩展欧几里得就可以求同余方程了

洛谷上用一道模板题,就是[NOIP2012 提高组] 同余方程

不过这道题其实是扩欧求逆元的板子题,只不过算作同余方程的模板也不算错

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

void exgcd(int a,int b,int &x,int &y)
{
    if(!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}

int inv(int a,int p)
{
    int x,y;
    exgcd(a,p,x,y);
    return (x%p+p)%p;
}

int a,b;

int main()
{
    scanf("%d%d",&a,&b);
    printf("%d",inv(a,b));
    return 0;
}
posted @ 2022-03-16 20:47  yhang323  阅读(94)  评论(0编辑  收藏  举报