欧几里得与扩展欧几里得
博客食用 , 效果更佳: 欧几里得 与 扩展欧几里得 - 核融合炉心 - 洛谷博客
以下,是本题需要的所有前置知识(大概吧wwww),
如果您已经学会了所有的前置知识,
请忽略 \(Baka\) 阿空的废话
直接看关于本题的内容
目录:
- 1.欧几里得定理
- 证明
- 应用
2.裴蜀定理
- 3.扩展欧几里得
- 证明
- 求解线性不定方程
4.关于本题
1.欧几里得定理:
即 : \(\large gcd(a,b) = gcd(b,a\mod b)\)
当 \(b=0\) 时,\(gcd(a,b)= \mid a\mid\) ;
感谢cyh学长给的证明 :
设 : \(a,b\) 的最大公约数为 \(c\)
则 : \(a=nc\ ,\ b=mc\) , \((n,m \in Z)\) , \(a=k\times b + r\)
则 : \(r=a\%b=a-k b=nc-kmc=(n-km)c\)
若要使 \((a,b) = (b,a\%b)\) ,
则需要证 : \(b , r\) 的最大公约数也为 \(c\) ,
即 : \(b=mc , r=(n-km)c\) 中 , \(m,(n-km)\) 互质 。
子证明:
用反证法 , 设存在 \(d\) 为 \(m,(n-km)\) 的最大公约数,且 \(d > 1\)。
设 : \(n-km=qd\) , \(m=pd\)
则 : \(b = mc = pdc\\) ,
\(\ a=kb+r=kpdc + qdc=dc(kp+q)\)
则 : \(a\) 还存在一个因数 \(dc > c\)
此结论与\(a,b\) 的最大公约数为 \(c\) 相矛盾
故 : 不存在 \(d>1\) 作为 \(m,(n-km)\) 的最大公约数
则 : \(m,(n-km)\)互质 ,子证明成立。
由子证明 ,得: \(b=mc , r=(n-km)c\) 中 , \(m,(n-km)\) 互质 。
则: \(b\) 与 \(r\) 的最大公因数仍为 \(c\)
证毕 。
应用:
有了以上的知识,我们便可以写出一个求\(a,b\) 的 \(gcd(a,b)\) 的函数了
由 欧几里得定理可得 , 要通过递归来求得\(gcd(a,b)\)的值
递归边界即: 当 \(b=0\) 时,\(gcd(a,b)=a\) ;
简单的代码实现:
int gcd(int a,int b) { if(b) return gcd(b,a%b); else return a; }
也可以写成一行:
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
2. 裴蜀定理:
设 \(a,b\) 为不全为 \(0\) 的整数 , 则存在整数 \(x,y\) ,使得 \(\large ax + by = gcd(a,b)\) 。
特别地 , \(gcd(a,b)=1\) \(\Leftrightarrow\)
存在 \(x,y \in Z\) , 使: \(ax+by = 1\) ;
那么该如何证明 定理的正确性 呢 ? 请继续往下看:
3. 欧几里得扩展:
对于不完全为 \(0\) 的两个数 \(a,b\) ,必然存在 无限个\(x,y\) ,使方程
\(\large ax + by = gcd(a,b)\)成立
证明:
设\(a>b\)
当$b=0 $时 , \(gcd(a,b)=a\) ,此时有唯一的解 : \(x=1,y=0\);
当\(a\times b\not= 0\) 时 ,
根据欧几里得定理 : $ gcd(a,b) = gcd(b,a% b)$ , 可知:
\(\underline{ax + by }= gcd(a,b) = gcd(b,a\% b)=\underline{b {x1} + (a\% b) y1}\)
( 设\(x1,y1\)是满足如上情况的一组解 )
而:\(a\% b = a-\lfloor \frac{a}{b}\rfloor \times b\)
则: 原等式可转化为:
①. \(ax + by = bx1 + (a-\lfloor \frac{a}{b}\rfloor \times b)y1\)
②. \(ax + by = bx1 + ay1 -\lfloor \frac{a}{b}\rfloor by 1\)
③. \(ax + by = ay1 +b(x1-\lfloor \frac{a}{b}\rfloor y 1)\)
由③,可得:
\(x = y1\)
$y=x1- \lfloor\frac{a}{b}\rfloor y1 $
这样 , 就可以得到 \(ax+by = c\) 的一组解
方程其他整数解 \(xi,yi\) 满足 :
$xi = x + \frac{b}{gcd(a,b)} \times t $
$yi = y + \frac{a}{gcd(a,b)} \times t $
其中 , \(t \in Z\)
代码实现
要想写一个求解 \(ax + by = gcd(a,b)\) 的程序
通过以上的证明 , 显然 , 可以通过递归实现
递归边界即 : 当$b=0 $时 , \(gcd(a,b)=a\) ,此时有唯一的解 : \(x=1,y=0\);
代码:cpp int exgcd(int a,int b,int d,int &x,int &y) { if(!b) { x=1 , y=0; return a; } d=exgcd(b,a % b,x,y); int tmp=x; x=y , y=tmp-a/b*y; }
求解不定方程:
对于不定方程 : $ ax + by = c $ :
根据 扩展欧几里得 , 可知 :
对于方程\(ax + by = gcd(a,b)\) , 有无数组解 \(x,y\).
则 :
1.若$ c% gcd(a,b) \not= 0 $ , 则原方程无解。
2.若\(c \% gcd(a,b) = 0\) :
设\(d = gcd(a,b)\) , 则原方程可转化为 :
\(a\times (x\times \frac{d}{c}) + b\times (y\times \frac{d}{c}) =d (= c\times \frac{d}{c})\)
换元,使 \(x1=(x\times \frac{d}{c})\) , \(y1=(y\times \frac{d}{c})\);
解方程: \(ax1 + by1 = d\) , 有无数组解 \(x1,y1\),
求解出 \(x1,y1\) 后 , 可得:
\(x=x1 \times \frac{c}{d}\) , \(y=y1 \times \frac{c}{d}\)
即可得到原方程 \(ax + by = c\) 的解
4.关于本题:
(啰嗦这么多终于开始说正事了)
题目中的三种操作:
①将酒桶中的酒倒入容积为\(b\ ml\)的酒杯中;
②将容积为\(a\ ml\)的酒杯中的酒倒入酒桶;
③将容积为\(b\ ml\)的酒杯中的酒倒入容积为\(a\ ml\)的酒杯中。题意分析:
设 最后剩余的最小酒量 为 \(ans\)
每次添加 \(b\ ml\)的酒 , 每次倒出 \(a\ ml\) 的酒则有:
$ans = -ax_1 + by_1 (x_1,y_1 \in Z) $欲使此不定方程有解
则 \(ans = k\times \gcd(a,b) \ (k\in Z)\)
而要求 \(ans\)的 最小非负整数解
故\(ans = gcd(a,b)\)算法实现:
用扩展欧几里得
解不定方程 \(\gcd(a,b) = ax_1 + by_1\)
得到了 一组 \(x_1\) 与 \(y_1\) 的解然后使 \(x_1\times(-1)\ ,\ a\times(-1)\);
原方程转化为:
\(\underline{ax_1 + by_1} = (-a)\times(-x_1) + by_1 = \underline{-ax + by}\)
得到:
\(x= -x_1\)
\(y=y_1\)然后通过
cpp while(x<0 || y<0) x+= (x<0 )?b/gcd:0, y-= (x>=0)?a/gcd:0;
将 \(x,y\) 都转化为最小的非负整数解
即得答案
附 \(AC\) 代码
#include<cstdio> #include<ctype.h> //==================================================== int a,b,x,y,gcd; //==================================================== inline int read() { int fl=1,w=0;char ch=getchar(); while(!isdigit(ch) && ch!='-') ch=getchar(); if(ch=='-') fl=-1; while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();} return fl*w; } void exgcd(int a,int b)//扩展欧几里得求线性不定方程的解 { if(b) { exgcd(b,a%b); int k=x; x=y , y=k-a/b*y; } else x=1,y=0,gcd=a; return ; } //==================================================== signed main() { a=read(),b=read(); exgcd(a,b); x*=-1,a*=-1;//进行转化 while(x<0 || y<0)//获得最小的非负整数解 x+= (x<0 )?b/gcd:0, y-= (x>=0)?a/gcd:0; printf("%d\n%d %d",gcd,x,y); }