1.1.3 最大公约数
主要内容:辗转相除法、二进制算法、最小公倍数、扩展欧几里得算法、求解线性同余方程
一、 辗转相除法
辗转相除法也称欧几里得算法。在稍后,我们会学到扩展欧几里得算法,其是在欧几里得算法的基础上发展的。
下面给出代码,相关证明可以参考《高等代数》第一章多项式中求最大公因式的证明
cpp
int gcd(int x, int y){
if (x==0) return y;
if (y==0) return x;
return gcd(y,x%y);
}
二、 二进制算法
一般的辗转相除法效率较低,下面给出利用因数2优化的辗转相除法。
cpp
inline int gcd(int x, int y){
if (x==0) return y;
if (y==0) return x;
int i,j,k;
for (i=0;(x&1)==0;i++) x>>=1;//除去x中所有因子2
for (j=0;(x&1)==0;j++) y>>=1;//除去y中所有因子2
k = min(i,j);//算出公共因子2的次数
while(1){
if (x<y) swap(x,y);
if ((x-=y)==0) return y<<i;
while ((x&1)==0) x>>=1; //去掉冗余的因子2
}
}
根据我的理解,2也可以更换为其他的因数,从而产生相似的算法,但二进制算法的优势在于,其有因子2,二进制移位运算相较四则运算和取模运算较为快速,因此我们采用二进制算法
三、最小公倍数
易证下面的结论
a\\cdot b = (a,b) \\cdot \[a,b\]\\\\ 其中(a,b)表示a和b的最大公约数,\[a,b\]表示a,b的最小公倍数\\\\ 从而有\\\\ \[a,b\] = \\frac{ab}{(a,b)}\\\\
四、 扩展欧几里得算法
首先,我们应当了解一个著名的定理:
裴蜀定理
\\begin{aligned} 对a,b\\in \\mathbb{Z},(a,b) = d\\quad\&\\Leftrightarrow\\quad\\exists x,y\\in\\mathbb{Z}\\quad\\quad ax+by = kd\\quad(k\\in \\mathbb{Z}-\\big\\{0\\big\\})\\\\ 特别地,令k=1:(a,b) = d\\quad\&\\Leftrightarrow\\quad\\exists x,y\\in\\mathbb{Z}\\quad s.t.ax+by=d\\\\ 推论,当k=d=1:(a,b)=1\\quad\&\\Leftrightarrow\\quad\\exists x,y\\in\\mathbb{Z}\\quad s.t.ax+by=1 \\end{aligned}
我们主要考察第二个等式
对于给定的a,b,我们可以通过欧几里得算法求得d
但是如何求出对应的p,q,使得pa+qb=d呢?
这就需要用到扩展欧几里得算法。
下面先给出推导过程
\\begin{aligned} 由(a,b)=d,可设p_1a+q_1b\&=d\\\\ 再由(b,a\\%b)=d,可设p_2b+q_2(a\\%b)\&=d\\\\ 令t=\[\\frac{a}{b}\],则a\\%b\&=a-bt\\\\ 即p_2b+q_2(a-bt)\&=d\\\\ 乘开后合并得q_2a+(p_2-q_2t)b\&=d\\\\ \\end{aligned} \\begin{aligned} 于是,p_1\&=q_2\\\\ q_1\&=p_2-q_2t=p_2-q_2\[\\frac{a}{b}\]\\\\ 于是,我们找到了(p_1,q_1)\&与(p_2,q_2)的关系,\\\\ \\quad\\quad我们可以将其作为递归公式使用:\\\\ (p_n,q_n)\&=(q_{n+1},p_{n+1}-q_{n+1}\[\\frac{a_n}{b_n}\]) \\end{aligned}
再考虑边界条件:
a_n\\ne0,b_n=0\\left\\{ \\begin{matrix} p_n=1 \\\\ q_n=0 \\end{matrix} \\right.\\\\ a_n=0,b_n\\ne0\\left\\{ \\begin{matrix} p_n=0 \\\\ q_n=1 \\end{matrix} \\right.\\\\
于是,我们得到了下面的算法
如果a_n\\ne0,b_n=0\\left\\{ \\begin{matrix} \\begin{aligned} (p_n,q_n)\&=(1,0) \\\\ gcd(a_n,b_n)\&=a_n \\end{aligned} \\end{matrix} \\right.\\\\ 如果a_n=0,b_n\\ne0\\left\\{ \\begin{matrix} \\begin{aligned} (p_n,q_n)\&=(0,1) \\\\ gcd(a_n,b_n)\&=b_n \\end{aligned} \\end{matrix} \\right.\\\\ 否则,令(a_{n+1},b_{n+1})=(b_n,a_n\\%b_n)\\left\\{ \\begin{matrix} \\begin{aligned} (p_n,q_n)\&=(q_{n+1},p_{n+1}-q_{n+1}\[\\frac{a_n}{b_n}\])\\\\ gcd(a_n,b_n)\&=gcd(b,a\\%b) \\end{aligned} \\end{matrix} \\right.\\\\ 其中,n可以理解为程序当前所在的递归层数\\\\
将上述大括号内容的程序合并在一个函数中,我们就得到了扩展欧几里得算法
代码如下:
cpp
int exgcd(int a, int b, int &p, int &q){
//两种边界情况
if (!a){
p = 0, q = 1;
return b;
}
if (!b){
p = 1, q = 0;
return a;
}
//不处于边界情况,准备递归
int d = exgcd(b, a%b, p, q);//递推,回归时p,q的层数为n+1
int p2 = p, q2 = q; //存储第n+1层的p,q的信息
p=q2, q = p2 - q2 * (a / b);//计算第n层的p,q的信息;
return d; //返回gcd(a,b)
}
五、求解线性同余方程
借助扩展欧几里得算法,我们可以求解线性同余方程
pa \\equiv c \\quad(mod\\quad b)
求解该方程等价于求解
pa + qb = c
很显然,当c是d=(a,b)的倍数时,该方程有解。
于是此方程可用扩欧算法求解,
\\begin{aligned} 先对p_0a+q_0b\&=d解出p_0和q_0\\\\ 再设c\&=kd=k\\cdot(a,b)\\\\ 则kp_0a+kq_0b\&=kd=c\\\\ 即p=kp_0\&,q=kq_0 \\end{aligned}
cpp
bool linrequ(int a, int b, int c, int &p, int &q){
int d = exgcd(a, b, p, q);
if (c % d) return false; //如果c不整除d,则无整数解
int k = c / d;
p *= k; q *= k;
return true;
}
需要注意的是,由于原同余方程中不存在q,所以同余方程中可能有多组解。
根据本原解p,q可以构造出更多的解
\\begin{aligned} \\forall t \\in \\mathbb{Z}\\quad(p_n,q_n) \&= (p+tb,q-ta)\\quad\\\\ 都是方程\\quad pa \&\\equiv c \\quad(mod\\quad b)\\quad的解 \\end{aligned}
本文由"小苏打NaHCO3"原创,未经允许,严禁转载!
如果觉得本文对你有用,不妨点赞收藏一下吧!