含有两个未知数,并且含有未知数的项的次数都是1的整式方程叫做二元一次方程。所有二元一次方程都可化为ax+by+c=0(a、b≠0)的一般式与ax+by=c(a、b≠0)的标准式,否则不为二元一次方程。
适合一个二元一次方程的每一对未知数的值,叫做这个二元一次方程的一个解。每个二元一次方程都有无数对方程的解,由二元一次方程组成的二元一次方程组才可能有唯一解,二元一次方程组常用加减消元法或代入消元法转换为一元一次方程进行求解。
众所周知,解一个单一的二元一次方程是十分困难的,只能用一种简单的方法————枚举(暴力出奇迹)。可是这是不可能,因为我们的时间复杂度是爆炸的,所以我们需要一种时间复杂度低的算法来解决这种困难。
那么是时候引申出此博客的主角了——拓展欧几里得算法。可是在提出此前,我们需要掌握一个简单数学知识(最大公因数与最小公倍数)。
具体我就不解释了,自己百度一下,而我们主要讲的就是它们的求法,我在这里只讲一种——欧几里得算法(辗转相除法)。顾名思义,这是与欧几里得有关的。这是求两个自然数之间的最大公因数。
为什么?我们先提出一个定理(两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数)。
证法如下:
a可以表示成a = kb + r(a,b,k,r皆为正整数,且r
假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。
而r = a - kb,两边同时除以d,r/d=a/d-kb/d=m,由等式右边可知m为整数,因此d|r。
因此d也是b,a mod b的公约数。
假设d是b,a mod b的公约数, 则d|b,d| (a-k*b),k是一个整数。
进而d|a.因此d也是a,b的公约数。
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。
好了,现在我们亮出代码吧:
int gcd(int a,int b)//a,b为两个数。
{
if(!b)
return a;
else
{
int r=gcd(b,a%b);
return r;
}
}
那么回归正题,如何用这个东西来求出解。
顾名思义,这是欧几里得算法的升级法。
可是这与二元一次方程有什么关系呢?
这是学长的解释:
这是解释,可我们该如何用代码实现呢?
毕竟是拓展,还是有一点的相似,所以我们的代码大致不变,只需要引入两个变量来表示解,根据学长所说的,我们只需要在最后将x置为1,y为0就可以了。其实还有一步,在辗转相除时,y - = ( a / b ) * x,至于为什么,我就不解释了。
下面是代码:
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
else
{
ll r=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return r;
}
}
当然这只能在c(方程的常数)能被(a,b)整除时才有效,不然就没有整数解。
在最后别忘了将倍数乘回x,y(方程的解)。
当然这只能求出一组解,至于如何用特解求出普遍解,请听学长解释:
而我就不在这里写代码了,请读者自行实现。
其它数论博客:
C++初等数论同余——欧拉函数与CRT
C++基础数论————排列组合