拓展欧几里得算法

拓展欧几里得

  • 问题:求方程 \(ax+by=gcd(a,b)\)\(x,y\) 的整数解

  • 预备知识:根据某个数学证明,方程 \(ax+by=c\) 有解当且仅当 \(c\equiv0\pmod{gcd(a,b)}\) 。所以只要方程有解,就可以通过求出方程 \(ax+by=gcd(a,b)\) 的解,来求方程 \(ax+by=c\) 的解。

  • 拓展欧几里得 \(exgcd\):普通的欧几里得算法就是辗转相除,而拓展欧几里得就是利用了辗转相除的一些性质,提出了一种求不定方程特解的算法。

首先,对于不定方程 \(ax+by=gcd(a,b)\)

假设存在一组解 \((x1,y1)\) 满足

\(bx_1+(a\bmod b)y_1=gcd(b,a\bmod b)\)

\(gcd\) 的性质可得,\(gcd(a,b)=gcd(b,a\bmod b)\)

所以 \(ax+by=bx_1+(a\bmod b)y_1\)

\(a\bmod b\) 实际上就是 \(a-a/b \times b\)

于是可以整理出:

\(ax+by=ay_1+b(x_1-(a/b)y_1)\)

由于 \(x1,y1\) 已知,所以就找到了关于 \(x,y\) 的一组解:

\(\begin{cases}x=y_1\\y=x_1-(a/b)y_1\end{cases}\)

然后就结束了。


然而还有一个很重要的问题,

\(x_1,y_1\) 怎么求???

再把前面的两个式子拿过来看看:

\(\begin{cases}ax+by=gcd(a,b)&1\\bx_1+(a\bmod b)y_1=gcd(b,a\bmod b)&2\end{cases}\)

想要求方程1的解,就要先知道方程2的解。

想要知道方程2的解,就要知道与之对应的一个方程3的解。

很明显的递归结构。

可以观察到,每一次方程的系数都在变小,所以递归一定有一个终点—— \(b=0\),类似于求 \(gcd\) 的函数。

而当 \(b=0\) 时,\(gcd(a,b)=gcd(a,0)=a\)

所以只要 \(x\) 取1,\(y\) 取任意值皆可(最好取0)

所以就可以开始回溯,回溯的时候每一次都用上文所提的通解:

\(\begin{cases}x=y_1\\y=x_1-(a/b)y_1\end{cases}\)

然后就真的结束了。。。

例题:P1082 同余方程

代码:大致模板

/*
问题:求不定方程 ax+by==1 的整数解 
*/
#include<bits/stdc++.h>
using namespace std;
void exgcd(int a,int b,int &x,int &y){//拓欧
    if(b==0){
        x=1;y=0;//递归尽头,b==0 的情况 
        return;
    }
    exgcd(b,a%b,x,y);//改变系数,继续递归 
    int tmp=y;
    y=x-(a/b)*y;
    x=tmp;//代用 x,y x1,y1 的通解公式 
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int main(){
    int a,b,x,y;
    scanf("%d%d",&a,&b);
    if(gcd(a,b)>1){//如果两个数不互质,无解 
		puts("No solution");
		return 0;
	}
    exgcd(a,b,x,y);
    printf("The solution of the problem is x=%d y=%d\n",x,y);
}
posted @ 2019-11-05 16:16  TangzYoung  阅读(236)  评论(0编辑  收藏  举报